477 lines
17 KiB
HTML
477 lines
17 KiB
HTML
<!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 consistency and documentation purposes.
|
|
</div>
|
|
|
|
<h3>1.6 Direct Float Storage</h3>
|
|
|
|
<p>The system now stores all timestamp fields directly as float values in the database:</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.
|
|
All timestamp fields remain as float values.
|
|
|
|
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
|
|
|
|
# All fields (including timestamps) remain in their original format
|
|
# No conversions needed - timestamps are already float
|
|
|
|
return data
|
|
</div>
|
|
|
|
<p><strong>Key Benefits:</strong></p>
|
|
<ul>
|
|
<li><strong>No unnecessary conversions</strong> between float and string</li>
|
|
<li><strong>Direct database storage</strong> as float values</li>
|
|
<li><strong>Consistent data types</strong> across the entire system</li>
|
|
<li><strong>Better performance</strong> without string parsing</li>
|
|
</ul>
|
|
|
|
<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 as float</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 Storage</td>
|
|
<td><span class="phase-status phase-completed">COMPLETED</span></td>
|
|
<td>All timestamps stored as float (no string conversion)</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>
|