From 7ad3ec577b21486b127b05b1fd27cb32225776a9 Mon Sep 17 00:00:00 2001 From: Madison Date: Fri, 27 Jun 2025 11:54:59 -0500 Subject: [PATCH] Python prep --- docs/docs-platform.yml | 53 ++++++ docs/scripts/generate-docs.js | 112 ++++++++++-- docs/src/components/mdx/card.tsx | 8 +- .../authentication/api-setup.mdx | 165 ++++++++++++++++++ .../templates-python/authentication/index.mdx | 56 ++++++ .../templates-python/authentication/meta.json | 10 ++ docs/templates-python/integration/django.mdx | 6 + docs/templates-python/integration/meta.json | 10 ++ docs/templates-python/meta.json | 23 +++ .../team-management/meta.json | 10 ++ .../user-management/meta.json | 10 ++ 11 files changed, 452 insertions(+), 11 deletions(-) create mode 100644 docs/templates-python/authentication/api-setup.mdx create mode 100644 docs/templates-python/authentication/index.mdx create mode 100644 docs/templates-python/authentication/meta.json create mode 100644 docs/templates-python/integration/django.mdx create mode 100644 docs/templates-python/integration/meta.json create mode 100644 docs/templates-python/meta.json create mode 100644 docs/templates-python/team-management/meta.json create mode 100644 docs/templates-python/user-management/meta.json diff --git a/docs/docs-platform.yml b/docs/docs-platform.yml index fb955f465..c71310ea9 100644 --- a/docs/docs-platform.yml +++ b/docs/docs-platform.yml @@ -259,3 +259,56 @@ pages: - path: others/cli-authentication.mdx platforms: ["python"] # Python only + # Python-specific content from templates-python/ + # Authentication section + - path: authentication/api-setup.mdx + platforms: ["python"] + + - path: authentication/user-sessions.mdx + platforms: ["python"] + + - path: authentication/oauth-flows.mdx + platforms: ["python"] + + - path: authentication/server-validation.mdx + platforms: ["python"] + + # User Management section + - path: user-management/create-users.mdx + platforms: ["python"] + + - path: user-management/user-operations.mdx + platforms: ["python"] + + - path: user-management/custom-user-data.mdx + platforms: ["python"] + + - path: user-management/user-permissions.mdx + platforms: ["python"] + + # Team Management section + - path: team-management/team-operations.mdx + platforms: ["python"] + + - path: team-management/team-invitations.mdx + platforms: ["python"] + + - path: team-management/team-permissions.mdx + platforms: ["python"] + + - path: team-management/organization-management.mdx + platforms: ["python"] + + # Framework Integration section + - path: integration/django.mdx + platforms: ["python"] + + - path: integration/flask.mdx + platforms: ["python"] + + - path: integration/fastapi.mdx + platforms: ["python"] + + - path: integration/standalone-scripts.mdx + platforms: ["python"] + diff --git a/docs/scripts/generate-docs.js b/docs/scripts/generate-docs.js index 671cd492e..768872c54 100644 --- a/docs/scripts/generate-docs.js +++ b/docs/scripts/generate-docs.js @@ -10,6 +10,7 @@ const __dirname = path.dirname(__filename); // Configure paths const TEMPLATE_DIR = path.resolve(__dirname, '../templates'); +const PYTHON_TEMPLATE_DIR = path.resolve(__dirname, '../templates-python'); const OUTPUT_BASE_DIR = path.resolve(__dirname, '../content/docs'); const CONFIG_FILE = path.resolve(__dirname, '../docs-platform.yml'); const PLATFORMS = ['next', 'react', 'js', 'python']; @@ -147,11 +148,21 @@ function generateMetaFiles() { const folderName = getFolderName(platform); const platformDisplayName = getPlatformDisplayName(platform); - // Find all meta.json files in the template directory - const metaFiles = glob.sync('**/meta.json', { cwd: TEMPLATE_DIR }); + // For Python platform, prioritize Python-specific templates, but also include shared templates + const templateDir = (platform === 'python' && fs.existsSync(PYTHON_TEMPLATE_DIR)) ? PYTHON_TEMPLATE_DIR : TEMPLATE_DIR; + // Find all meta.json files in the appropriate template directory + const metaFiles = glob.sync('**/meta.json', { cwd: templateDir }); + + // For Python, also get meta.json files from shared templates (excluding root meta.json to avoid conflicts) + let sharedMetaFiles = []; + if (platform === 'python' && fs.existsSync(PYTHON_TEMPLATE_DIR)) { + sharedMetaFiles = glob.sync('**/meta.json', { cwd: TEMPLATE_DIR }).filter(file => file !== 'meta.json'); + } + + // Process Python-specific meta files for (const metaFile of metaFiles) { - const srcPath = path.join(TEMPLATE_DIR, metaFile); + const srcPath = path.join(templateDir, metaFile); const destPath = path.join(OUTPUT_BASE_DIR, folderName, metaFile); // If this is a nested meta.json (not root), check if the folder should exist for this platform @@ -220,9 +231,15 @@ function generateMetaFiles() { // Regular page else { // Check if this is actually a folder reference vs a page reference - // A folder reference should have a corresponding directory in templates - const folderPath = path.join(TEMPLATE_DIR, page); - const isActualFolder = fs.existsSync(folderPath) && fs.statSync(folderPath).isDirectory(); + // Check both template directories for Python + let folderPath = path.join(templateDir, page); + let isActualFolder = fs.existsSync(folderPath) && fs.statSync(folderPath).isDirectory(); + + // For Python, also check shared templates directory + if (!isActualFolder && platform === 'python') { + folderPath = path.join(TEMPLATE_DIR, page); + isActualFolder = fs.existsSync(folderPath) && fs.statSync(folderPath).isDirectory(); + } if (isActualFolder) { // This is a folder reference - check if folder has content for this platform @@ -264,6 +281,32 @@ function generateMetaFiles() { fs.writeFileSync(destPath, JSON.stringify(metaData, null, 2)); console.log(`Generated platform-specific meta.json for ${platform}: ${destPath}`); } + + // For Python, also process shared meta.json files (but not root) + for (const metaFile of sharedMetaFiles) { + const folderPath = path.dirname(metaFile); + + // Check if any pages in this folder are included for Python + const hasContentInFolder = platformConfig.pages && platformConfig.pages.some(configPage => + configPage.path.startsWith(`${folderPath}/`) && + configPage.platforms.includes(platform) + ); + + if (hasContentInFolder) { + const srcPath = path.join(TEMPLATE_DIR, metaFile); + const destPath = path.join(OUTPUT_BASE_DIR, folderName, metaFile); + + // Read and copy the shared meta.json + const templateContent = fs.readFileSync(srcPath, 'utf8'); + + // Create directory if it doesn't exist + fs.mkdirSync(path.dirname(destPath), { recursive: true }); + + // Write the shared meta.json + fs.writeFileSync(destPath, templateContent); + console.log(`Generated shared meta.json for ${platform}: ${destPath}`); + } + } } } @@ -273,6 +316,7 @@ function generateMetaFiles() { function copyAssets() { const assetDirs = ['imgs']; + // Copy assets from main templates directory for (const dir of assetDirs) { const srcDir = path.join(TEMPLATE_DIR, dir); @@ -295,13 +339,35 @@ function copyAssets() { } } } + + // Copy Python-specific assets if they exist + if (fs.existsSync(PYTHON_TEMPLATE_DIR)) { + for (const dir of assetDirs) { + const srcDir = path.join(PYTHON_TEMPLATE_DIR, dir); + + if (fs.existsSync(srcDir)) { + const destDir = path.join(OUTPUT_BASE_DIR, 'python', dir); + fs.mkdirSync(destDir, { recursive: true }); + + // Find and copy all files + const files = glob.sync('**/*', { cwd: srcDir, nodir: true }); + for (const file of files) { + const srcFile = path.join(srcDir, file); + const destFile = path.join(destDir, file); + fs.mkdirSync(path.dirname(destFile), { recursive: true }); + fs.copyFileSync(srcFile, destFile); + console.log(`Copied Python-specific asset: ${srcFile} -> ${destFile}`); + } + } + } + } } /** * Main function to generate platform-specific docs */ function generateDocs() { - // Find all MDX files in the template directory + // Find all MDX files in the main template directory const templateFiles = glob.sync('**/*.mdx', { cwd: TEMPLATE_DIR }); if (templateFiles.length === 0) { @@ -309,9 +375,9 @@ function generateDocs() { return; } - console.log(`Found ${templateFiles.length} template files`); + console.log(`Found ${templateFiles.length} shared template files`); - // Process for each platform + // Process shared templates for each platform for (const platform of PLATFORMS) { const folderName = getFolderName(platform); const outputDir = path.join(OUTPUT_BASE_DIR, folderName); @@ -319,7 +385,7 @@ function generateDocs() { // Create the output directory fs.mkdirSync(outputDir, { recursive: true }); - // Process each template file + // Process each shared template file for (const file of templateFiles) { // Check if this file should be included for this platform if (!shouldIncludeFileForPlatform(platform, file)) { @@ -346,6 +412,32 @@ function generateDocs() { } } + // Process Python-specific templates if they exist + if (fs.existsSync(PYTHON_TEMPLATE_DIR)) { + console.log(`Processing Python-specific templates from ${PYTHON_TEMPLATE_DIR}`); + const pythonTemplateFiles = glob.sync('**/*.mdx', { cwd: PYTHON_TEMPLATE_DIR }); + + if (pythonTemplateFiles.length > 0) { + const pythonOutputDir = path.join(OUTPUT_BASE_DIR, 'python'); + + for (const file of pythonTemplateFiles) { + const inputFile = path.join(PYTHON_TEMPLATE_DIR, file); + const outputFile = path.join(pythonOutputDir, file); + + // Read the Python-specific template + const templateContent = fs.readFileSync(inputFile, 'utf8'); + + // Create output directory if it doesn't exist + fs.mkdirSync(path.dirname(outputFile), { recursive: true }); + + // Write the content (no platform processing needed for Python-specific files) + fs.writeFileSync(outputFile, templateContent); + + console.log(`Generated Python-specific: ${outputFile}`); + } + } + } + // Generate meta.json files for navigation generateMetaFiles(); diff --git a/docs/src/components/mdx/card.tsx b/docs/src/components/mdx/card.tsx index bcd3b6e68..2b828aa02 100644 --- a/docs/src/components/mdx/card.tsx +++ b/docs/src/components/mdx/card.tsx @@ -1,6 +1,6 @@ 'use client'; -import { Code, FileText, Play, Puzzle } from 'lucide-react'; +import { Code, FileText, Link as LinkIcon, Play, Puzzle, Settings, Shield, UserCheck } from 'lucide-react'; import Link from 'next/link'; import { type ReactNode } from 'react'; import { cn } from '../../lib/cn'; @@ -11,6 +11,12 @@ const iconMap: Record> = { 'fa-solid fa-puzzle': Puzzle, 'fa-regular fa-file-lines': FileText, 'fa-solid fa-code': Code, + + // Authentication-related icons + 'settings': Settings, + 'user-check': UserCheck, + 'link': LinkIcon, + 'shield-check': Shield, }; export type CardProps = { diff --git a/docs/templates-python/authentication/api-setup.mdx b/docs/templates-python/authentication/api-setup.mdx new file mode 100644 index 000000000..43bbfa14a --- /dev/null +++ b/docs/templates-python/authentication/api-setup.mdx @@ -0,0 +1,165 @@ +--- +title: API Setup & Configuration +--- + +This guide covers the essential setup for using Stack Auth's REST API in your Python application. Stack Auth provides a REST API for managing users, sessions, and authentication flows. + +## Prerequisites + +Before you begin, make sure you have: +- A Stack Auth [project](https://app.stack-auth.com/projects) created +- Python 3.7+ installed +- `requests` library (`pip install requests`) + +## Environment Configuration + +First, set up your API credentials. Get these from your Stack Auth dashboard: + +```bash +# .env file +STACK_PROJECT_ID=your_project_id +STACK_PUBLISHABLE_KEY=your_publishable_key +STACK_SECRET_KEY=your_secret_key +STACK_API_URL=https://api.stack-auth.com +``` + +## Basic API Client Setup + +Create a basic API client to handle Stack Auth requests: + +```python +import os +import requests +from typing import Dict, Any, Optional + +class StackAuthClient: + def __init__(self): + self.project_id = os.getenv('STACK_PROJECT_ID') + self.secret_key = os.getenv('STACK_SECRET_KEY') + self.publishable_key = os.getenv('STACK_PUBLISHABLE_KEY') + self.api_url = os.getenv('STACK_API_URL', 'https://api.stack-auth.com') + + if not all([self.project_id, self.secret_key]): + raise ValueError("Missing required Stack Auth credentials") + + def _make_request( + self, + method: str, + endpoint: str, + data: Optional[Dict[str, Any]] = None, + headers: Optional[Dict[str, str]] = None + ) -> requests.Response: + """Make authenticated request to Stack Auth API""" + url = f"{self.api_url}/api/v1{endpoint}" + + # Add authentication headers + auth_headers = { + 'X-Stack-Project-Id': self.project_id, + 'X-Stack-Secret-Key': self.secret_key, + 'Content-Type': 'application/json' + } + + if headers: + auth_headers.update(headers) + + response = requests.request( + method=method, + url=url, + json=data, + headers=auth_headers + ) + + # Handle common error cases + if response.status_code == 401: + raise Exception("Authentication failed - check your API credentials") + elif response.status_code == 403: + raise Exception("Forbidden - insufficient permissions") + elif not response.ok: + raise Exception(f"API request failed: {response.status_code} - {response.text}") + + return response + + def get(self, endpoint: str, **kwargs) -> requests.Response: + return self._make_request('GET', endpoint, **kwargs) + + def post(self, endpoint: str, data: Dict[str, Any] = None, **kwargs) -> requests.Response: + return self._make_request('POST', endpoint, data, **kwargs) + + def put(self, endpoint: str, data: Dict[str, Any] = None, **kwargs) -> requests.Response: + return self._make_request('PUT', endpoint, data, **kwargs) + + def delete(self, endpoint: str, **kwargs) -> requests.Response: + return self._make_request('DELETE', endpoint, **kwargs) + +# Initialize the client +stack_client = StackAuthClient() +``` + +## Testing Your Setup + +Test your API connection with a simple request: + +```python +def test_connection(): + try: + # Test with a simple API call + response = stack_client.get('/users') + print("✅ Connection successful!") + print(f"Found {len(response.json().get('users', []))} users") + return True + except Exception as e: + print(f"❌ Connection failed: {e}") + return False + +# Run the test +if __name__ == "__main__": + test_connection() +``` + +## Error Handling + +Implement proper error handling for production use: + +```python +import logging +from typing import Optional + +logger = logging.getLogger(__name__) + +class StackAuthError(Exception): + """Base exception for Stack Auth errors""" + pass + +class StackAuthClient: + # ... previous code ... + + def safe_request( + self, + method: str, + endpoint: str, + data: Optional[Dict[str, Any]] = None + ) -> Optional[Dict[str, Any]]: + """Make a safe request with comprehensive error handling""" + try: + response = self._make_request(method, endpoint, data) + return response.json() + except requests.exceptions.ConnectionError: + logger.error("Failed to connect to Stack Auth API") + raise StackAuthError("Network connection failed") + except requests.exceptions.Timeout: + logger.error("Request to Stack Auth API timed out") + raise StackAuthError("Request timed out") + except Exception as e: + logger.error(f"Stack Auth API error: {e}") + raise StackAuthError(f"API request failed: {e}") +``` + +## Next Steps + +With your API client set up, you can now: + +- [Authenticate users](./user-sessions) with your application +- [Handle OAuth flows](./oauth-flows) for social login +- [Validate server-side sessions](./server-validation) + +For detailed API reference, see the [REST API documentation](/api/overview). diff --git a/docs/templates-python/authentication/index.mdx b/docs/templates-python/authentication/index.mdx new file mode 100644 index 000000000..77699fd94 --- /dev/null +++ b/docs/templates-python/authentication/index.mdx @@ -0,0 +1,56 @@ +--- +title: "Authentication Flows" +description: "Learn how to implement authentication flows in your Python application using Stack Auth's REST API" +--- +This section covers the core authentication patterns and flows you'll need to implement secure authentication in your Python application using Stack Auth's REST API. + +## Core Authentication + + + + Set up your Python client and configure API credentials for Stack Auth integration. + + + + Handle user session management, validation, and lifecycle in Python applications. + + + + Implement OAuth authentication flows for social login providers via the REST API. + + + + Validate user tokens and authenticate requests on your Python backend. + + + +## Authentication Patterns + +Each authentication flow addresses specific use cases: + +- **API Setup**: Foundation for all authentication operations +- **User Sessions**: Managing authenticated user state and session lifecycle +- **OAuth Flows**: Social login integration (Google, GitHub, etc.) +- **Server Validation**: Securing your API endpoints and validating requests + +## Getting Started + +Start with [API Setup & Configuration](./api-setup) to establish your Python client, then proceed to the specific authentication flow that matches your application's needs. + +For framework-specific integration examples, see the [Framework Integration](../integration) section. diff --git a/docs/templates-python/authentication/meta.json b/docs/templates-python/authentication/meta.json new file mode 100644 index 000000000..2e17eee92 --- /dev/null +++ b/docs/templates-python/authentication/meta.json @@ -0,0 +1,10 @@ +{ + "title": "Authentication", + "description": "Authentication flows and patterns for Python applications", + "pages": [ + "api-setup", + "user-sessions", + "oauth-flows", + "server-validation" + ] +} diff --git a/docs/templates-python/integration/django.mdx b/docs/templates-python/integration/django.mdx new file mode 100644 index 000000000..965a185b1 --- /dev/null +++ b/docs/templates-python/integration/django.mdx @@ -0,0 +1,6 @@ +--- +title: Django Integration +description: Integrate Stack Auth with Django applications using middleware and decorators +--- + +This guide is coming soon. diff --git a/docs/templates-python/integration/meta.json b/docs/templates-python/integration/meta.json new file mode 100644 index 000000000..3d40d0dee --- /dev/null +++ b/docs/templates-python/integration/meta.json @@ -0,0 +1,10 @@ +{ + "title": "Framework Integration", + "description": "Stack Auth integration patterns for Python frameworks", + "pages": [ + "django", + "flask", + "fastapi", + "standalone-scripts" + ] +} diff --git a/docs/templates-python/meta.json b/docs/templates-python/meta.json new file mode 100644 index 000000000..13afd6788 --- /dev/null +++ b/docs/templates-python/meta.json @@ -0,0 +1,23 @@ +{ + "title": "Python Documentation", + "description": "Stack Auth for Python applications using the REST API", + "pages": [ + "overview", + "---Getting Started---", + "getting-started/setup", + "getting-started/auth-providers", + "---Authentication Flows---", + "authentication", + "---User Management---", + "user-management", + "---Team & Organization Management---", + "team-management", + "---Framework Integration---", + "integration", + "---Backend Concepts---", + "concepts/backend-integration", + "---Other Resources---", + "others", + "faq" + ] +} diff --git a/docs/templates-python/team-management/meta.json b/docs/templates-python/team-management/meta.json new file mode 100644 index 000000000..aa083602c --- /dev/null +++ b/docs/templates-python/team-management/meta.json @@ -0,0 +1,10 @@ +{ + "title": "Team Management", + "description": "Team and organization operations via the REST API", + "pages": [ + "team-operations", + "team-invitations", + "team-permissions", + "organization-management" + ] +} diff --git a/docs/templates-python/user-management/meta.json b/docs/templates-python/user-management/meta.json new file mode 100644 index 000000000..bdef5e5a9 --- /dev/null +++ b/docs/templates-python/user-management/meta.json @@ -0,0 +1,10 @@ +{ + "title": "User Management", + "description": "User operations and data management via the REST API", + "pages": [ + "create-users", + "user-operations", + "custom-user-data", + "user-permissions" + ] +}