Python prep

This commit is contained in:
Madison 2025-06-27 11:54:59 -05:00
parent 4d04d8e8ad
commit 7ad3ec577b
11 changed files with 452 additions and 11 deletions

View File

@ -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"]

View File

@ -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();

View File

@ -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<string, React.ComponentType<{ className?: string }>> = {
'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 = {

View File

@ -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).

View File

@ -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
<CardGroup cols={2}>
<Card
title="API Setup & Configuration"
href="./authentication/api-setup"
icon="settings"
>
Set up your Python client and configure API credentials for Stack Auth integration.
</Card>
<Card
title="User Sessions"
href="./user-sessions"
icon="user-check"
>
Handle user session management, validation, and lifecycle in Python applications.
</Card>
<Card
title="OAuth Flows"
href="./oauth-flows"
icon="link"
>
Implement OAuth authentication flows for social login providers via the REST API.
</Card>
<Card
title="Server Validation"
href="./server-validation"
icon="shield-check"
>
Validate user tokens and authenticate requests on your Python backend.
</Card>
</CardGroup>
## 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.

View File

@ -0,0 +1,10 @@
{
"title": "Authentication",
"description": "Authentication flows and patterns for Python applications",
"pages": [
"api-setup",
"user-sessions",
"oauth-flows",
"server-validation"
]
}

View File

@ -0,0 +1,6 @@
---
title: Django Integration
description: Integrate Stack Auth with Django applications using middleware and decorators
---
This guide is coming soon.

View File

@ -0,0 +1,10 @@
{
"title": "Framework Integration",
"description": "Stack Auth integration patterns for Python frameworks",
"pages": [
"django",
"flask",
"fastapi",
"standalone-scripts"
]
}

View File

@ -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"
]
}

View File

@ -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"
]
}

View File

@ -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"
]
}