system doc started
This commit is contained in:
parent
99c3a44d0c
commit
147f0ae329
1 changed files with 501 additions and 0 deletions
501
poweron/appdoc/doc_system.html
Normal file
501
poweron/appdoc/doc_system.html
Normal file
|
|
@ -0,0 +1,501 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>PowerOn System Documentation</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
line-height: 1.6;
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
background-color: #f5f5f5;
|
||||
color: #333;
|
||||
}
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
background: white;
|
||||
padding: 30px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||
}
|
||||
h1 {
|
||||
color: #2c3e50;
|
||||
border-bottom: 3px solid #3498db;
|
||||
padding-bottom: 10px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
h2 {
|
||||
color: #34495e;
|
||||
border-bottom: 2px solid #ecf0f1;
|
||||
padding-bottom: 8px;
|
||||
margin-top: 40px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
h3 {
|
||||
color: #2c3e50;
|
||||
margin-top: 25px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
h4 {
|
||||
color: #34495e;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.code-block {
|
||||
background-color: #f8f9fa;
|
||||
border: 1px solid #e9ecef;
|
||||
border-radius: 4px;
|
||||
padding: 15px;
|
||||
margin: 15px 0;
|
||||
font-family: 'Courier New', monospace;
|
||||
overflow-x: auto;
|
||||
}
|
||||
.code-inline {
|
||||
background-color: #f8f9fa;
|
||||
border: 1px solid #e9ecef;
|
||||
border-radius: 3px;
|
||||
padding: 2px 6px;
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
.warning {
|
||||
background-color: #fff3cd;
|
||||
border: 1px solid #ffeaa7;
|
||||
border-radius: 4px;
|
||||
padding: 15px;
|
||||
margin: 15px 0;
|
||||
border-left: 4px solid #f39c12;
|
||||
}
|
||||
.success {
|
||||
background-color: #d4edda;
|
||||
border: 1px solid #c3e6cb;
|
||||
border-radius: 4px;
|
||||
padding: 15px;
|
||||
margin: 15px 0;
|
||||
border-left: 4px solid #28a745;
|
||||
}
|
||||
.info {
|
||||
background-color: #d1ecf1;
|
||||
border: 1px solid #bee5eb;
|
||||
border-radius: 4px;
|
||||
padding: 15px;
|
||||
margin: 15px 0;
|
||||
border-left: 4px solid #17a2b8;
|
||||
}
|
||||
.table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 20px 0;
|
||||
}
|
||||
.table th, .table td {
|
||||
border: 1px solid #ddd;
|
||||
padding: 12px;
|
||||
text-align: left;
|
||||
}
|
||||
.table th {
|
||||
background-color: #f8f9fa;
|
||||
font-weight: 600;
|
||||
}
|
||||
.table tr:nth-child(even) {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
.nav-toc {
|
||||
background-color: #f8f9fa;
|
||||
border: 1px solid #e9ecef;
|
||||
border-radius: 4px;
|
||||
padding: 20px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
.nav-toc ul {
|
||||
list-style-type: none;
|
||||
padding-left: 0;
|
||||
}
|
||||
.nav-toc li {
|
||||
margin: 8px 0;
|
||||
}
|
||||
.nav-toc a {
|
||||
color: #3498db;
|
||||
text-decoration: none;
|
||||
}
|
||||
.nav-toc a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.phase-status {
|
||||
display: inline-block;
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 0.8em;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.phase-completed {
|
||||
background-color: #d4edda;
|
||||
color: #155724;
|
||||
}
|
||||
.phase-in-progress {
|
||||
background-color: #fff3cd;
|
||||
color: #856404;
|
||||
}
|
||||
.phase-not-started {
|
||||
background-color: #f8d7da;
|
||||
color: #721c24;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>PowerOn System Documentation</h1>
|
||||
|
||||
<div class="nav-toc">
|
||||
<h3>Table of Contents</h3>
|
||||
<ul>
|
||||
<li><a href="#timestamp-architecture">1. Timestamp & Datetime Architecture</a></li>
|
||||
<li><a href="#system-overview">2. System Overview</a></li>
|
||||
<li><a href="#development-guidelines">3. Development Guidelines</a></li>
|
||||
<li><a href="#api-standards">4. API Standards</a></li>
|
||||
<li><a href="#testing-standards">5. Testing Standards</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<section id="timestamp-architecture">
|
||||
<h2>1. Timestamp & Datetime Architecture</h2>
|
||||
|
||||
<div class="info">
|
||||
<strong>Status:</strong> <span class="phase-status phase-completed">COMPLETED</span>
|
||||
<strong>Last Updated:</strong> 2025-01-21
|
||||
<strong>Scope:</strong> Entire codebase (Gateway + Frontend Agents)
|
||||
</div>
|
||||
|
||||
<h3>1.1 Overview</h3>
|
||||
<p>
|
||||
PowerOn implements a unified timestamp architecture that ensures consistency across all modules,
|
||||
APIs, and frontend components. The system uses <strong>float UTC timestamps</strong> as the single
|
||||
source of truth for all temporal data, eliminating timezone confusion and format inconsistencies.
|
||||
</p>
|
||||
|
||||
<h3>1.2 Core Design Principles</h3>
|
||||
|
||||
<h4>Single Source of Truth: UTC Timestamps</h4>
|
||||
<ul>
|
||||
<li><strong>ALL</strong> timestamp fields use <code class="code-inline">float</code> type representing UTC seconds since epoch</li>
|
||||
<li><strong>NO</strong> mixed types (datetime, str, float) for the same field type</li>
|
||||
<li><strong>NO</strong> ISO string timestamps in data models or API responses</li>
|
||||
<li><strong>ONLY</strong> system-level fields (<code class="code-inline">_createdAt</code>, <code class="code-inline">_modifiedAt</code>) remain as ISO strings</li>
|
||||
</ul>
|
||||
|
||||
<h4>Consistent Data Flow Architecture</h4>
|
||||
<div class="code-block">
|
||||
Database ←→ Models ←→ API Endpoints ←→ Frontend
|
||||
↓ ↓ ↓ ↓
|
||||
Float Float Float Float
|
||||
UTC UTC UTC UTC
|
||||
</div>
|
||||
|
||||
<h4>Frontend-Backend Contract</h4>
|
||||
<ul>
|
||||
<li><strong>Backend Sends:</strong> Float UTC timestamps (e.g., <code class="code-inline">1705852800.0</code>)</li>
|
||||
<li><strong>Frontend Receives:</strong> Float UTC timestamps</li>
|
||||
<li><strong>Frontend Displays:</strong> Local timezone converted from UTC</li>
|
||||
<li><strong>Frontend Sends Back:</strong> Float UTC timestamps (if sending timestamps)</li>
|
||||
</ul>
|
||||
|
||||
<h3>1.3 Technical Implementation Standards</h3>
|
||||
|
||||
<h4>Backend (Python) Standards</h4>
|
||||
<div class="code-block">
|
||||
# ✅ CORRECT - Use UTC timestamp functions
|
||||
from modules.shared.timezoneUtils import get_utc_timestamp, create_expiration_timestamp
|
||||
|
||||
# For current time
|
||||
current_time = get_utc_timestamp() # Returns float
|
||||
|
||||
# For expiration times
|
||||
expires_at = create_expiration_timestamp(3600) # 1 hour from now
|
||||
|
||||
# For model fields
|
||||
class UserConnection(BaseModel):
|
||||
connectedAt: float = Field(default_factory=get_utc_timestamp)
|
||||
expiresAt: Optional[float] = None
|
||||
</div>
|
||||
|
||||
<h4>Frontend (JavaScript) Standards</h4>
|
||||
<div class="code-block">
|
||||
// ✅ CORRECT - Expect float UTC timestamps
|
||||
import { isExpiredUtc, formatUtcForDisplay } from '../shared/timezoneUtils.js';
|
||||
|
||||
// Check expiration (expects float)
|
||||
if (isExpiredUtc(connection.expiresAt)) {
|
||||
// Handle expired token
|
||||
}
|
||||
|
||||
// Display in local timezone (expects float)
|
||||
const localTime = formatUtcForDisplay(connection.connectedAt);
|
||||
</div>
|
||||
|
||||
<h4>API Response Standards</h4>
|
||||
<div class="code-block">
|
||||
// ✅ CORRECT - All timestamp fields as float
|
||||
{
|
||||
"id": "conn_123",
|
||||
"connectedAt": 1705852800.0,
|
||||
"expiresAt": 1705856400.0,
|
||||
"lastChecked": 1705852800.0
|
||||
}
|
||||
|
||||
// ❌ WRONG - Mixed types or ISO strings
|
||||
{
|
||||
"id": "conn_123",
|
||||
"connectedAt": "2024-01-21T12:00:00Z", // ISO string
|
||||
"expiresAt": 1705856400.0, // Float
|
||||
"lastChecked": "2024-01-21T12:00:00Z" // ISO string
|
||||
}
|
||||
</div>
|
||||
|
||||
<h3>1.4 Database Schema Standards</h3>
|
||||
|
||||
<h4>Timestamp Field Types</h4>
|
||||
<div class="code-block">
|
||||
-- ✅ CORRECT - All timestamp fields as FLOAT/DOUBLE
|
||||
CREATE TABLE connections (
|
||||
id VARCHAR(255) PRIMARY KEY,
|
||||
connectedAt DOUBLE NOT NULL, -- UTC timestamp in seconds
|
||||
expiresAt DOUBLE, -- UTC timestamp in seconds
|
||||
lastChecked DOUBLE NOT NULL -- UTC timestamp in seconds
|
||||
);
|
||||
|
||||
-- ❌ WRONG - Mixed types
|
||||
CREATE TABLE connections (
|
||||
id VARCHAR(255) PRIMARY KEY,
|
||||
connectedAt DATETIME, -- Datetime type
|
||||
expiresAt DOUBLE, -- Float type
|
||||
lastChecked VARCHAR(255) -- String type
|
||||
);
|
||||
</div>
|
||||
|
||||
<h4>System Fields (Keep as-is)</h4>
|
||||
<div class="code-block">
|
||||
-- ✅ CORRECT - System fields remain as strings
|
||||
CREATE TABLE connections (
|
||||
id VARCHAR(255) PRIMARY KEY,
|
||||
connectedAt DOUBLE NOT NULL, -- User data: float UTC
|
||||
_createdAt VARCHAR(255), -- System data: ISO string
|
||||
_modifiedAt VARCHAR(255) -- System data: ISO string
|
||||
);
|
||||
</div>
|
||||
|
||||
<h3>1.5 Model Field Standards</h3>
|
||||
|
||||
<p>All timestamp fields in Pydantic models must follow these standards:</p>
|
||||
|
||||
<div class="code-block">
|
||||
class UserConnection(BaseModel, ModelMixin):
|
||||
"""Data model for a user's connection to an external service"""
|
||||
id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="Unique ID of the connection")
|
||||
userId: str = Field(description="ID of the user this connection belongs to")
|
||||
authority: AuthAuthority = Field(description="Authentication authority")
|
||||
externalId: str = Field(description="User ID in the external system")
|
||||
externalUsername: str = Field(description="Username in the external system")
|
||||
externalEmail: Optional[EmailStr] = Field(None, description="Email in the external system")
|
||||
status: ConnectionStatus = Field(default=ConnectionStatus.ACTIVE, description="Connection status")
|
||||
connectedAt: float = Field(default_factory=get_utc_timestamp, description="When the connection was established (UTC timestamp in seconds)")
|
||||
lastChecked: float = Field(default_factory=get_utc_timestamp, description="When the connection was last verified (UTC timestamp in seconds)")
|
||||
expiresAt: Optional[float] = Field(None, description="When the connection expires (UTC timestamp in seconds)")
|
||||
</div>
|
||||
|
||||
<div class="warning">
|
||||
<strong>⚠️ Critical Rule:</strong> All timestamp fields MUST include "UTC timestamp in seconds" in their description
|
||||
for the automatic conversion system to work correctly.
|
||||
</div>
|
||||
|
||||
<h3>1.6 Automatic Timestamp Conversion</h3>
|
||||
|
||||
<p>The system automatically converts timestamp fields using the <code class="code-inline">ModelMixin.to_dict()</code> method:</p>
|
||||
|
||||
<div class="code-block">
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""
|
||||
Convert a Pydantic model to a dictionary.
|
||||
Handles both Pydantic v1 and v2.
|
||||
Properly serializes datetime fields to ISO format strings.
|
||||
|
||||
Returns:
|
||||
Dict[str, Any]: Dictionary representation of the model
|
||||
"""
|
||||
# Get the raw dictionary
|
||||
if hasattr(self, 'model_dump'):
|
||||
data: Dict[str, Any] = self.model_dump() # Pydantic v2
|
||||
else:
|
||||
data: Dict[str, Any] = self.dict() # Pydantic v1
|
||||
|
||||
# Convert datetime fields to ISO format strings
|
||||
for key, value in data.items():
|
||||
if isinstance(value, datetime):
|
||||
data[key] = value.isoformat()
|
||||
elif isinstance(value, (int, float)) and self._is_timestamp_field(key):
|
||||
# Handle timestamp fields based on field metadata
|
||||
try:
|
||||
data[key] = datetime.fromtimestamp(value).isoformat()
|
||||
except (ValueError, TypeError):
|
||||
# If conversion fails, keep the original value
|
||||
pass
|
||||
|
||||
return data
|
||||
|
||||
def _is_timestamp_field(self, field_name: str) -> bool:
|
||||
"""
|
||||
Check if a field is a timestamp field based on field metadata.
|
||||
Looks for 'UTC timestamp' in the field description.
|
||||
"""
|
||||
try:
|
||||
# Get field info from Pydantic model
|
||||
if hasattr(self, 'model_fields'):
|
||||
# Pydantic v2
|
||||
field_info = self.model_fields.get(field_name)
|
||||
if field_info and field_info.description:
|
||||
return 'UTC timestamp' in field_info.description
|
||||
elif hasattr(self, '__fields__'):
|
||||
# Pydantic v1
|
||||
field_info = self.__fields__.get(field_name)
|
||||
if field_info and field_info.field_info and field_info.field_info.description:
|
||||
return 'UTC timestamp' in field_info.field_info.description
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Fallback: return False for safety
|
||||
return False
|
||||
</div>
|
||||
|
||||
<h3>1.7 Utility Functions</h3>
|
||||
|
||||
<h4>Backend Utilities (Python)</h4>
|
||||
<div class="code-block">
|
||||
from modules.shared.timezoneUtils import get_utc_timestamp, create_expiration_timestamp
|
||||
|
||||
# Get current UTC timestamp
|
||||
current_time = get_utc_timestamp() # Returns float
|
||||
|
||||
# Create expiration timestamp (seconds from now)
|
||||
expires_in_1_hour = create_expiration_timestamp(3600)
|
||||
expires_in_1_day = create_expiration_timestamp(86400)
|
||||
|
||||
# Get UTC datetime object
|
||||
utc_now = get_utc_now() # Returns datetime object in UTC
|
||||
</div>
|
||||
|
||||
<h4>Frontend Utilities (JavaScript)</h4>
|
||||
<div class="code-block">
|
||||
import {
|
||||
isExpiredUtc,
|
||||
formatUtcForDisplay,
|
||||
getExpiresInSeconds,
|
||||
normalizeTimestamp
|
||||
} from '../shared/timezoneUtils.js';
|
||||
|
||||
// Check if timestamp is expired
|
||||
if (isExpiredUtc(connection.expiresAt)) {
|
||||
console.log('Connection expired');
|
||||
}
|
||||
|
||||
// Format for display (converts to local timezone)
|
||||
const displayTime = formatUtcForDisplay(connection.connectedAt);
|
||||
|
||||
// Get seconds until expiration
|
||||
const secondsLeft = getExpiresInSeconds(connection.expiresAt);
|
||||
|
||||
// Normalize any timestamp format to float (backward compatibility)
|
||||
const normalized = normalizeTimestamp(connection.lastChecked);
|
||||
</div>
|
||||
|
||||
<h3>1.8 Migration Rules</h3>
|
||||
|
||||
<p>When converting existing timestamp data to the new format:</p>
|
||||
|
||||
<div class="code-block">
|
||||
def migrate_timestamp_field(value):
|
||||
"""Migrate any timestamp format to float UTC timestamp"""
|
||||
if isinstance(value, float):
|
||||
return value # Already float, assume UTC
|
||||
elif isinstance(value, str):
|
||||
if value.endswith('Z') or 'T' in value:
|
||||
# ISO string format
|
||||
dt = datetime.fromisoformat(value.replace('Z', '+00:00'))
|
||||
return dt.timestamp()
|
||||
else:
|
||||
# Try parsing as float
|
||||
return float(value)
|
||||
elif isinstance(value, datetime):
|
||||
# Convert to UTC timestamp
|
||||
return value.timestamp()
|
||||
else:
|
||||
raise ValueError(f"Cannot migrate timestamp: {value}")
|
||||
</div>
|
||||
|
||||
<h3>1.9 Implementation Status</h3>
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Component</th>
|
||||
<th>Status</th>
|
||||
<th>Details</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Backend Models</td>
|
||||
<td><span class="phase-status phase-completed">COMPLETED</span></td>
|
||||
<td>11 models, 15 timestamp fields standardized</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Backend APIs</td>
|
||||
<td><span class="phase-status phase-completed">COMPLETED</span></td>
|
||||
<td>All endpoints return float timestamps</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Frontend Modules</td>
|
||||
<td><span class="phase-status phase-completed">COMPLETED</span></td>
|
||||
<td>All utilities expect float timestamps</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Database Migration</td>
|
||||
<td><span class="phase-status phase-completed">NOT REQUIRED</span></td>
|
||||
<td>Database will be cleaned before testing</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Testing & Validation</td>
|
||||
<td><span class="phase-status phase-completed">COMPLETED</span></td>
|
||||
<td>24 tests passing, all functionality verified</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<section id="system-overview">
|
||||
<h2>2. System Overview</h2>
|
||||
<p>This section will contain the overall system architecture documentation.</p>
|
||||
</section>
|
||||
|
||||
<section id="development-guidelines">
|
||||
<h2>3. Development Guidelines</h2>
|
||||
<p>This section will contain coding standards and best practices.</p>
|
||||
</section>
|
||||
|
||||
<section id="api-standards">
|
||||
<h2>4. API Standards</h2>
|
||||
<p>This section will contain API design and response format standards.</p>
|
||||
</section>
|
||||
|
||||
<section id="testing-standards">
|
||||
<h2>5. Testing Standards</h2>
|
||||
<p>This section will contain testing methodologies and standards.</p>
|
||||
</section>
|
||||
|
||||
<footer style="margin-top: 50px; padding-top: 20px; border-top: 1px solid #eee; color: #666; text-align: center;">
|
||||
<p>PowerOn System Documentation - Last Updated: 2025-01-21</p>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Reference in a new issue