security(p0): set strong ADMIN_PASSWORD in .env and sync DB admin hash; remove hardcoded 'admin123' in example; update TODO
This commit is contained in:
2
TODO.md
2
TODO.md
@@ -9,7 +9,7 @@
|
|||||||
### **Remove Hardcoded Credentials**
|
### **Remove Hardcoded Credentials**
|
||||||
- [x] **URGENT**: Remove `.env` file from git repository
|
- [x] **URGENT**: Remove `.env` file from git repository
|
||||||
- [x] **URGENT**: Generate new SECRET_KEY (32+ character random string)
|
- [x] **URGENT**: Generate new SECRET_KEY (32+ character random string)
|
||||||
- [ ] **URGENT**: Change default admin password from `admin123` to secure password
|
- [x] **URGENT**: Change default admin password from `admin123` to secure password
|
||||||
- [ ] **URGENT**: Implement proper environment variable management
|
- [ ] **URGENT**: Implement proper environment variable management
|
||||||
- [ ] **URGENT**: Add `.env` to `.gitignore` and commit
|
- [ ] **URGENT**: Add `.env` to `.gitignore` and commit
|
||||||
- [ ] **URGENT**: Document secret rotation procedures
|
- [ ] **URGENT**: Document secret rotation procedures
|
||||||
|
|||||||
268
examples/advanced_template_example.py
Normal file
268
examples/advanced_template_example.py
Normal file
@@ -0,0 +1,268 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Advanced Template Features Example
|
||||||
|
|
||||||
|
This script demonstrates how to use the enhanced template system
|
||||||
|
with conditional sections, loops, and rich formatting.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import httpx
|
||||||
|
import json
|
||||||
|
from datetime import date, datetime
|
||||||
|
|
||||||
|
|
||||||
|
class AdvancedTemplateExample:
|
||||||
|
def __init__(self, base_url="http://localhost:8000"):
|
||||||
|
self.base_url = base_url
|
||||||
|
self.auth_token = None
|
||||||
|
|
||||||
|
async def authenticate(self, username="admin", password=None):
|
||||||
|
"""Authenticate and get access token"""
|
||||||
|
async with httpx.AsyncClient() as client:
|
||||||
|
from os import getenv
|
||||||
|
if password is None:
|
||||||
|
password = getenv("ADMIN_PASSWORD", "")
|
||||||
|
response = await client.post(
|
||||||
|
f"{self.base_url}/api/auth/token",
|
||||||
|
data={"username": username, "password": password}
|
||||||
|
)
|
||||||
|
if response.status_code == 200:
|
||||||
|
self.auth_token = response.json()["access_token"]
|
||||||
|
print("✅ Authentication successful")
|
||||||
|
else:
|
||||||
|
print(f"❌ Authentication failed: {response.text}")
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
@property
|
||||||
|
def headers(self):
|
||||||
|
if not self.auth_token:
|
||||||
|
raise ValueError("Not authenticated")
|
||||||
|
return {"Authorization": f"Bearer {self.auth_token}"}
|
||||||
|
|
||||||
|
async def test_variable_formatting(self):
|
||||||
|
"""Test variable formatting without generating documents"""
|
||||||
|
print("\n📝 Testing Variable Formatting...")
|
||||||
|
|
||||||
|
test_cases = [
|
||||||
|
{"value": "1234.56", "format": "currency", "expected": "$1,234.56"},
|
||||||
|
{"value": "2023-12-25", "format": "date:%B %d, %Y", "expected": "December 25, 2023"},
|
||||||
|
{"value": "5551234567", "format": "phone", "expected": "(555) 123-4567"},
|
||||||
|
{"value": "hello world", "format": "title", "expected": "Hello World"},
|
||||||
|
{"value": "75.5", "format": "percentage:1", "expected": "75.5%"}
|
||||||
|
]
|
||||||
|
|
||||||
|
async with httpx.AsyncClient() as client:
|
||||||
|
for test in test_cases:
|
||||||
|
response = await client.post(
|
||||||
|
f"{self.base_url}/api/templates/test-formatting",
|
||||||
|
headers=self.headers,
|
||||||
|
data={
|
||||||
|
"variable_value": test["value"],
|
||||||
|
"format_spec": test["format"]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
result = response.json()
|
||||||
|
actual = result["formatted_result"]
|
||||||
|
status = "✅" if actual == test["expected"] else "❌"
|
||||||
|
print(f" {status} {test['value']} | {test['format']} → {actual}")
|
||||||
|
else:
|
||||||
|
print(f" ❌ Error testing {test['format']}: {response.text}")
|
||||||
|
|
||||||
|
async def demonstrate_template_features(self):
|
||||||
|
"""Demonstrate advanced template features with sample data"""
|
||||||
|
print("\n🎯 Demonstrating Advanced Template Features...")
|
||||||
|
|
||||||
|
# Sample context data that would come from database
|
||||||
|
sample_context = {
|
||||||
|
"CLIENT_NAME": "Smith Family Trust",
|
||||||
|
"FILE_NO": "2023-1001",
|
||||||
|
"ATTORNEY": "Jane Doe, Esq.",
|
||||||
|
"TODAY": date.today().isoformat(),
|
||||||
|
"CLIENT_BALANCE": 2500.00,
|
||||||
|
"total_amount": 8750.00,
|
||||||
|
"payment_terms": "Net 30",
|
||||||
|
|
||||||
|
# Sample services for loop demonstration
|
||||||
|
"services": [
|
||||||
|
{
|
||||||
|
"description": "Estate Planning Consultation",
|
||||||
|
"date": "2023-11-15",
|
||||||
|
"hours": 2.5,
|
||||||
|
"rate": 350.00,
|
||||||
|
"amount": 875.00
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Trust Document Preparation",
|
||||||
|
"date": "2023-11-20",
|
||||||
|
"hours": 6.0,
|
||||||
|
"rate": 350.00,
|
||||||
|
"amount": 2100.00
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Document Review and Revision",
|
||||||
|
"date": "2023-11-25",
|
||||||
|
"hours": 3.5,
|
||||||
|
"rate": 350.00,
|
||||||
|
"amount": 1225.00
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Client Meeting and Execution",
|
||||||
|
"date": "2023-11-30",
|
||||||
|
"hours": 2.0,
|
||||||
|
"rate": 350.00,
|
||||||
|
"amount": 700.00
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
# Sample payments for another loop
|
||||||
|
"payments": [
|
||||||
|
{"date": "2023-11-01", "amount": 1000.00, "method": "Check"},
|
||||||
|
{"date": "2023-11-15", "amount": 1500.00, "method": "Wire Transfer"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
print("Sample context data:")
|
||||||
|
print(json.dumps(sample_context, indent=2, default=str))
|
||||||
|
|
||||||
|
return sample_context
|
||||||
|
|
||||||
|
async def get_formatting_help(self):
|
||||||
|
"""Get and display formatting help"""
|
||||||
|
print("\n📚 Available Formatting Options...")
|
||||||
|
|
||||||
|
async with httpx.AsyncClient() as client:
|
||||||
|
response = await client.get(
|
||||||
|
f"{self.base_url}/api/templates/formatting-help",
|
||||||
|
headers=self.headers
|
||||||
|
)
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
help_data = response.json()
|
||||||
|
|
||||||
|
print("\n🔧 Formatting Options:")
|
||||||
|
for category, info in help_data["formatting_options"].items():
|
||||||
|
print(f"\n {category.upper()}:")
|
||||||
|
print(f" Description: {info['description']}")
|
||||||
|
if 'syntax' in info:
|
||||||
|
print(f" Syntax: {info['syntax']}")
|
||||||
|
if 'examples' in info:
|
||||||
|
print(" Examples:")
|
||||||
|
for example in info['examples'][:2]: # Show first 2 examples
|
||||||
|
print(f" {example['input']} | {example['format']} → {example['output']}")
|
||||||
|
|
||||||
|
print("\n📝 Template Syntax:")
|
||||||
|
for syntax_type, syntax in help_data["template_syntax"].items():
|
||||||
|
print(f" {syntax_type}: {syntax}")
|
||||||
|
else:
|
||||||
|
print(f"❌ Error getting help: {response.text}")
|
||||||
|
|
||||||
|
async def create_sample_template_content(self):
|
||||||
|
"""Generate sample template content for demonstration"""
|
||||||
|
template_content = """
|
||||||
|
LEGAL SERVICES INVOICE
|
||||||
|
|
||||||
|
Client: {{ CLIENT_NAME | title }}
|
||||||
|
File Number: {{ FILE_NO }}
|
||||||
|
Attorney: {{ ATTORNEY }}
|
||||||
|
Date: {{ TODAY | date }}
|
||||||
|
|
||||||
|
{% if CLIENT_BALANCE > 0 %}
|
||||||
|
⚠️ NOTICE: Outstanding balance of {{ CLIENT_BALANCE | currency }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
SERVICES PROVIDED:
|
||||||
|
{% for service in services %}
|
||||||
|
{{ service_index }}. {{ service.description }}
|
||||||
|
Date: {{ service.date | date:%m/%d/%Y }}
|
||||||
|
Hours: {{ service.hours | number:1 }}
|
||||||
|
Rate: {{ service.rate | currency }}
|
||||||
|
Amount: {{ service.amount | currency }}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
SUBTOTAL: {{ format_currency(total_amount) }}
|
||||||
|
|
||||||
|
{% if payments %}
|
||||||
|
PAYMENTS RECEIVED:
|
||||||
|
{% for payment in payments %}
|
||||||
|
- {{ payment.date | date:%m/%d/%Y }}: {{ payment.amount | currency }} ({{ payment.method }})
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if payment_terms %}
|
||||||
|
PAYMENT TERMS: {{ payment_terms }}
|
||||||
|
{% else %}
|
||||||
|
Payment due within 30 days of invoice date.
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if CLIENT_BALANCE > 1000 %}
|
||||||
|
Please remit payment immediately to avoid service interruption.
|
||||||
|
{% else %}
|
||||||
|
Thank you for your prompt payment.
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
Generated on {{ TODAY | date }} by the Delphi Database System
|
||||||
|
"""
|
||||||
|
|
||||||
|
print("\n📄 Sample Template Content:")
|
||||||
|
print("=" * 60)
|
||||||
|
print(template_content)
|
||||||
|
print("=" * 60)
|
||||||
|
|
||||||
|
return template_content
|
||||||
|
|
||||||
|
async def run_demonstration(self):
|
||||||
|
"""Run the complete demonstration"""
|
||||||
|
print("🚀 Advanced Template Features Demonstration")
|
||||||
|
print("=" * 50)
|
||||||
|
|
||||||
|
# Authenticate
|
||||||
|
if not await self.authenticate():
|
||||||
|
return
|
||||||
|
|
||||||
|
# Show available formatting options
|
||||||
|
await self.get_formatting_help()
|
||||||
|
|
||||||
|
# Test individual formatting functions
|
||||||
|
await self.test_variable_formatting()
|
||||||
|
|
||||||
|
# Show sample template and context
|
||||||
|
context = await self.demonstrate_template_features()
|
||||||
|
template_content = await self.create_sample_template_content()
|
||||||
|
|
||||||
|
print("\n✨ This demonstrates the following advanced features:")
|
||||||
|
print(" • Conditional sections ({% if %} blocks)")
|
||||||
|
print(" • Loop sections ({% for %} blocks)")
|
||||||
|
print(" • Variable formatting ({{ var | format }})")
|
||||||
|
print(" • Template functions ({{ format_currency() }})")
|
||||||
|
print(" • Rich context data with nested objects")
|
||||||
|
|
||||||
|
print("\n🔄 To use these features in your application:")
|
||||||
|
print(" 1. Create a DOCX template with the syntax above")
|
||||||
|
print(" 2. Upload it via /api/templates/upload")
|
||||||
|
print(" 3. Generate documents via /api/templates/{id}/generate-advanced")
|
||||||
|
print(" 4. Optionally convert to PDF by setting output_format: 'PDF'")
|
||||||
|
|
||||||
|
print("\n✅ Advanced template features are ready to use!")
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
"""Main demonstration function"""
|
||||||
|
demo = AdvancedTemplateExample()
|
||||||
|
await demo.run_demonstration()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print("Advanced Template Features Example")
|
||||||
|
print("Make sure the Delphi Database server is running on http://localhost:8000")
|
||||||
|
print()
|
||||||
|
|
||||||
|
try:
|
||||||
|
asyncio.run(main())
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("\n👋 Demonstration interrupted")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"\n❌ Error: {e}")
|
||||||
Reference in New Issue
Block a user