1018 lines
24 KiB
Markdown
1018 lines
24 KiB
Markdown
# Initial Project Files Design
|
|
|
|
## Purpose
|
|
|
|
This document specifies the exact contents of all initial configuration and bootstrap files needed to create a working StarPunk project from scratch. Developer agents should use this document to create the complete initial project structure with correct file contents.
|
|
|
|
## Philosophy
|
|
|
|
These files represent the minimal bootstrap needed to:
|
|
- Define project dependencies
|
|
- Configure the development environment
|
|
- Provide setup instructions
|
|
- Establish git workflow
|
|
- Initialize the Flask application
|
|
|
|
Every file listed here is essential for V1.
|
|
|
|
---
|
|
|
|
## Configuration Files
|
|
|
|
### .gitignore
|
|
|
|
**Location**: `/home/phil/Projects/starpunk/.gitignore`
|
|
|
|
**Purpose**: Prevent committing sensitive data, user content, and build artifacts.
|
|
|
|
**Contents**:
|
|
```gitignore
|
|
# Python
|
|
__pycache__/
|
|
*.py[cod]
|
|
*$py.class
|
|
*.so
|
|
.Python
|
|
build/
|
|
develop-eggs/
|
|
dist/
|
|
downloads/
|
|
eggs/
|
|
.eggs/
|
|
lib/
|
|
lib64/
|
|
parts/
|
|
sdist/
|
|
var/
|
|
wheels/
|
|
*.egg-info/
|
|
.installed.cfg
|
|
*.egg
|
|
MANIFEST
|
|
|
|
# Virtual Environment
|
|
.venv/
|
|
venv/
|
|
env/
|
|
ENV/
|
|
env.bak/
|
|
venv.bak/
|
|
|
|
# Environment Configuration (CRITICAL - CONTAINS SECRETS)
|
|
.env
|
|
*.env
|
|
!.env.example
|
|
|
|
# User Data (CRITICAL - NEVER COMMIT)
|
|
data/
|
|
*.db
|
|
*.sqlite
|
|
*.sqlite3
|
|
*.db-journal
|
|
|
|
# IDE
|
|
.vscode/
|
|
.idea/
|
|
*.swp
|
|
*.swo
|
|
*~
|
|
.DS_Store
|
|
*.iml
|
|
|
|
# Testing
|
|
.pytest_cache/
|
|
.coverage
|
|
htmlcov/
|
|
*.cover
|
|
.hypothesis/
|
|
.tox/
|
|
.nox/
|
|
|
|
# Logs
|
|
*.log
|
|
logs/
|
|
|
|
# OS
|
|
Thumbs.db
|
|
.directory
|
|
```
|
|
|
|
**Critical Rules**:
|
|
- `data/` MUST be gitignored (contains user notes and database)
|
|
- `.env` MUST be gitignored (contains secrets)
|
|
- `.env.example` is explicitly allowed (template)
|
|
|
|
---
|
|
|
|
### .env.example
|
|
|
|
**Location**: `/home/phil/Projects/starpunk/.env.example`
|
|
|
|
**Purpose**: Template for `.env` file. Committed to repository.
|
|
|
|
**Contents**:
|
|
```bash
|
|
# StarPunk Configuration Template
|
|
# Copy this file to .env and fill in your values
|
|
# DO NOT commit .env to version control
|
|
|
|
# =============================================================================
|
|
# SITE CONFIGURATION
|
|
# =============================================================================
|
|
|
|
# Public URL where your site is hosted (no trailing slash)
|
|
SITE_URL=http://localhost:5000
|
|
|
|
# Your site name (appears in RSS feed and page titles)
|
|
SITE_NAME=My StarPunk Site
|
|
|
|
# Your name (appears as author in RSS feed)
|
|
SITE_AUTHOR=Your Name
|
|
|
|
# Site description (appears in RSS feed)
|
|
SITE_DESCRIPTION=My personal IndieWeb site
|
|
|
|
# =============================================================================
|
|
# AUTHENTICATION
|
|
# =============================================================================
|
|
|
|
# Your IndieWeb identity URL (REQUIRED)
|
|
# This is YOUR personal website URL that you authenticate with
|
|
# Example: https://yourname.com or https://github.com/yourname
|
|
ADMIN_ME=https://your-website.com
|
|
|
|
# Session secret key (REQUIRED - GENERATE A RANDOM VALUE)
|
|
# Generate with: python3 -c "import secrets; print(secrets.token_hex(32))"
|
|
SESSION_SECRET=REPLACE_WITH_RANDOM_SECRET
|
|
|
|
# Session lifetime in days (default: 30)
|
|
SESSION_LIFETIME=30
|
|
|
|
# IndieLogin service URL (usually don't change this)
|
|
INDIELOGIN_URL=https://indielogin.com
|
|
|
|
# =============================================================================
|
|
# DATA STORAGE
|
|
# =============================================================================
|
|
|
|
# Base data directory (relative to project root)
|
|
DATA_PATH=./data
|
|
|
|
# Notes directory (where markdown files are stored)
|
|
NOTES_PATH=./data/notes
|
|
|
|
# SQLite database path
|
|
DATABASE_PATH=./data/starpunk.db
|
|
|
|
# =============================================================================
|
|
# FLASK CONFIGURATION
|
|
# =============================================================================
|
|
|
|
# Environment: development or production
|
|
FLASK_ENV=development
|
|
|
|
# Debug mode: 1 (on) or 0 (off)
|
|
# NEVER use debug mode in production
|
|
FLASK_DEBUG=1
|
|
|
|
# Flask secret key (falls back to SESSION_SECRET if not set)
|
|
FLASK_SECRET_KEY=
|
|
|
|
# =============================================================================
|
|
# DEVELOPMENT OPTIONS
|
|
# =============================================================================
|
|
|
|
# Log level: DEBUG, INFO, WARNING, ERROR, CRITICAL
|
|
LOG_LEVEL=INFO
|
|
|
|
# Enable SQL query logging (development only)
|
|
SQL_ECHO=0
|
|
```
|
|
|
|
**Usage**:
|
|
```bash
|
|
# Copy to .env
|
|
cp .env.example .env
|
|
|
|
# Edit .env with your values
|
|
nano .env
|
|
```
|
|
|
|
---
|
|
|
|
### requirements.txt
|
|
|
|
**Location**: `/home/phil/Projects/starpunk/requirements.txt`
|
|
|
|
**Purpose**: Production dependencies (exactly 6 packages per ADR-002).
|
|
|
|
**Contents**:
|
|
```
|
|
# StarPunk Production Dependencies
|
|
# Python 3.11+ required
|
|
|
|
# Web Framework
|
|
Flask==3.0.*
|
|
|
|
# Content Processing
|
|
markdown==3.5.*
|
|
|
|
# Feed Generation
|
|
feedgen==1.0.*
|
|
|
|
# HTTP Client (for IndieAuth)
|
|
httpx==0.27.*
|
|
|
|
# Configuration Management
|
|
python-dotenv==1.0.*
|
|
|
|
# Testing Framework
|
|
pytest==8.0.*
|
|
```
|
|
|
|
**Version Strategy**:
|
|
- Use `==X.Y.*` for minor version pinning
|
|
- Allows patch updates (security fixes)
|
|
- Prevents breaking changes from major/minor updates
|
|
|
|
**Installation**:
|
|
```bash
|
|
uv pip install -r requirements.txt
|
|
```
|
|
|
|
---
|
|
|
|
### requirements-dev.txt
|
|
|
|
**Location**: `/home/phil/Projects/starpunk/requirements-dev.txt`
|
|
|
|
**Purpose**: Development and testing dependencies.
|
|
|
|
**Contents**:
|
|
```
|
|
# StarPunk Development Dependencies
|
|
# Includes code quality and testing tools
|
|
|
|
# Include production dependencies
|
|
-r requirements.txt
|
|
|
|
# Testing
|
|
pytest-cov>=5.0.0 # Test coverage reporting
|
|
pytest-mock>=3.12.0 # Mocking for tests
|
|
|
|
# Code Quality
|
|
black>=24.0.0 # Code formatting
|
|
flake8>=7.0.0 # Linting
|
|
mypy>=1.8.0 # Type checking
|
|
|
|
# WSGI Server (for production-like testing)
|
|
gunicorn>=21.2.0
|
|
```
|
|
|
|
**Installation**:
|
|
```bash
|
|
uv pip install -r requirements-dev.txt
|
|
```
|
|
|
|
---
|
|
|
|
### README.md
|
|
|
|
**Location**: `/home/phil/Projects/starpunk/README.md`
|
|
|
|
**Purpose**: User-facing project documentation.
|
|
|
|
**Contents**:
|
|
```markdown
|
|
# StarPunk
|
|
|
|
A minimal, self-hosted IndieWeb CMS for publishing notes with RSS syndication.
|
|
|
|
## Philosophy
|
|
|
|
"Every line of code must justify its existence. When in doubt, leave it out."
|
|
|
|
StarPunk is designed for a single user who wants to:
|
|
- Publish short notes to their personal website
|
|
- Own their content (notes stored as portable markdown files)
|
|
- Syndicate via RSS
|
|
- Support IndieWeb standards (Micropub, IndieAuth)
|
|
- Run on minimal resources
|
|
|
|
## Features
|
|
|
|
- **File-based storage**: Notes are markdown files, owned by you
|
|
- **IndieAuth authentication**: Use your own website as identity
|
|
- **Micropub support**: Publish from any Micropub client
|
|
- **RSS feed**: Automatic syndication
|
|
- **No database lock-in**: SQLite for metadata, files for content
|
|
- **Self-hostable**: Run on your own server
|
|
- **Minimal dependencies**: 6 core dependencies, no build tools
|
|
|
|
## Requirements
|
|
|
|
- Python 3.11 or higher
|
|
- 500MB disk space
|
|
- Linux, macOS, or Windows with WSL2
|
|
|
|
## Quick Start
|
|
|
|
```bash
|
|
# Clone repository
|
|
git clone https://github.com/YOUR_USERNAME/starpunk.git
|
|
cd starpunk
|
|
|
|
# Install uv (package manager)
|
|
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
|
|
# Create virtual environment
|
|
uv venv .venv --python 3.11
|
|
|
|
# Install dependencies
|
|
uv pip install -r requirements.txt
|
|
|
|
# Configure
|
|
cp .env.example .env
|
|
# Edit .env and set ADMIN_ME and SESSION_SECRET
|
|
|
|
# Initialize database
|
|
mkdir -p data/notes
|
|
.venv/bin/python -c "from starpunk.database import init_db; init_db()"
|
|
|
|
# Run development server
|
|
.venv/bin/flask --app app.py run --debug
|
|
|
|
# Visit http://localhost:5000
|
|
```
|
|
|
|
## Configuration
|
|
|
|
All configuration is in the `.env` file. Required settings:
|
|
|
|
- `ADMIN_ME` - Your IndieWeb identity URL (e.g., https://yoursite.com)
|
|
- `SESSION_SECRET` - Random secret key (generate with `python3 -c "import secrets; print(secrets.token_hex(32))"`)
|
|
- `SITE_URL` - Public URL of your site
|
|
|
|
See `.env.example` for all options.
|
|
|
|
## Project Structure
|
|
|
|
```
|
|
starpunk/
|
|
├── app.py # Application entry point
|
|
├── starpunk/ # Application code
|
|
├── data/ # Your notes and database (gitignored)
|
|
│ ├── notes/ # Markdown files
|
|
│ └── starpunk.db # SQLite database
|
|
├── static/ # CSS and JavaScript
|
|
├── templates/ # HTML templates
|
|
└── tests/ # Test suite
|
|
```
|
|
|
|
## Usage
|
|
|
|
### Publishing Notes
|
|
|
|
**Via Web Interface**:
|
|
1. Navigate to `/admin`
|
|
2. Login with your IndieWeb identity
|
|
3. Create notes in markdown
|
|
|
|
**Via Micropub Client**:
|
|
1. Configure client with your site URL
|
|
2. Authenticate via IndieAuth
|
|
3. Publish from any Micropub-compatible app
|
|
|
|
### Backing Up Your Data
|
|
|
|
Your notes are stored as plain markdown files in `data/notes/`. Back up this directory:
|
|
|
|
```bash
|
|
# Simple backup
|
|
tar -czf backup.tar.gz data/
|
|
|
|
# Or use rsync
|
|
rsync -av data/ /backup/starpunk/
|
|
```
|
|
|
|
## Development
|
|
|
|
See [docs/standards/development-setup.md](docs/standards/development-setup.md) for detailed setup.
|
|
|
|
```bash
|
|
# Install dev dependencies
|
|
uv pip install -r requirements-dev.txt
|
|
|
|
# Run tests
|
|
.venv/bin/pytest
|
|
|
|
# Format code
|
|
.venv/bin/black starpunk/ tests/
|
|
|
|
# Lint
|
|
.venv/bin/flake8 starpunk/ tests/
|
|
```
|
|
|
|
## Architecture
|
|
|
|
StarPunk uses a hybrid storage approach:
|
|
- **Notes content**: Markdown files (portable, human-readable)
|
|
- **Metadata**: SQLite database (fast queries)
|
|
|
|
This gives you both portability AND performance.
|
|
|
|
See [docs/architecture/](docs/architecture/) for complete documentation.
|
|
|
|
## IndieWeb Compliance
|
|
|
|
StarPunk implements:
|
|
- [Micropub](https://micropub.spec.indieweb.org/) - Publishing API
|
|
- [IndieAuth](https://indieauth.spec.indieweb.org/) - Authentication
|
|
- [Microformats2](http://microformats.org/) - Semantic HTML markup
|
|
- [RSS 2.0](https://www.rssboard.org/rss-specification) - Feed syndication
|
|
|
|
## Deployment
|
|
|
|
### Production Setup
|
|
|
|
```bash
|
|
# Install gunicorn
|
|
uv pip install gunicorn
|
|
|
|
# Run with gunicorn
|
|
.venv/bin/gunicorn -w 4 -b 127.0.0.1:8000 app:app
|
|
|
|
# Configure nginx/Caddy for HTTPS
|
|
# Set up systemd for process management
|
|
# Enable regular backups of data/ directory
|
|
```
|
|
|
|
See [docs/architecture/deployment.md](docs/architecture/deployment.md) for details.
|
|
|
|
## License
|
|
|
|
MIT License - see LICENSE file
|
|
|
|
## Credits
|
|
|
|
Built with:
|
|
- [Flask](https://flask.palletsprojects.com/) - Web framework
|
|
- [python-markdown](https://python-markdown.github.io/) - Markdown processing
|
|
- [feedgen](https://feedgen.kiesow.be/) - RSS generation
|
|
- [httpx](https://www.python-httpx.org/) - HTTP client
|
|
- [IndieLogin](https://indielogin.com/) - Authentication service
|
|
|
|
## Contributing
|
|
|
|
This is a personal project optimized for single-user use. If you want additional features, consider forking!
|
|
|
|
## Support
|
|
|
|
- Documentation: [docs/](docs/)
|
|
- Issues: GitHub Issues
|
|
- IndieWeb: [indieweb.org](https://indieweb.org/)
|
|
```
|
|
|
|
---
|
|
|
|
## Application Files
|
|
|
|
### app.py
|
|
|
|
**Location**: `/home/phil/Projects/starpunk/app.py`
|
|
|
|
**Purpose**: Main application entry point.
|
|
|
|
**Contents**:
|
|
```python
|
|
"""
|
|
StarPunk - Minimal IndieWeb CMS
|
|
Main application entry point
|
|
"""
|
|
|
|
from starpunk import create_app
|
|
|
|
app = create_app()
|
|
|
|
if __name__ == '__main__':
|
|
# Development server
|
|
# For production, use: gunicorn app:app
|
|
app.run(debug=True)
|
|
```
|
|
|
|
**Rationale**: Minimal entry point. All logic is in the `starpunk` package.
|
|
|
|
---
|
|
|
|
### starpunk/__init__.py
|
|
|
|
**Location**: `/home/phil/Projects/starpunk/starpunk/__init__.py`
|
|
|
|
**Purpose**: Package initialization and Flask app factory.
|
|
|
|
**Contents**:
|
|
```python
|
|
"""
|
|
StarPunk package initialization
|
|
Creates and configures the Flask application
|
|
"""
|
|
|
|
from flask import Flask
|
|
from pathlib import Path
|
|
|
|
|
|
def create_app(config=None):
|
|
"""
|
|
Application factory for StarPunk
|
|
|
|
Args:
|
|
config: Optional configuration dict to override defaults
|
|
|
|
Returns:
|
|
Configured Flask application instance
|
|
"""
|
|
app = Flask(
|
|
__name__,
|
|
static_folder='../static',
|
|
template_folder='../templates'
|
|
)
|
|
|
|
# Load configuration
|
|
from starpunk.config import load_config
|
|
load_config(app, config)
|
|
|
|
# Initialize database
|
|
from starpunk.database import init_db
|
|
init_db(app)
|
|
|
|
# Register blueprints
|
|
# TODO: Implement blueprints in separate modules
|
|
# from starpunk.routes import public, admin, api
|
|
# app.register_blueprint(public.bp)
|
|
# app.register_blueprint(admin.bp)
|
|
# app.register_blueprint(api.bp)
|
|
|
|
# Error handlers
|
|
@app.errorhandler(404)
|
|
def not_found(error):
|
|
return {'error': 'Not found'}, 404
|
|
|
|
@app.errorhandler(500)
|
|
def server_error(error):
|
|
return {'error': 'Internal server error'}, 500
|
|
|
|
return app
|
|
|
|
|
|
# Package version
|
|
__version__ = '0.1.0'
|
|
```
|
|
|
|
---
|
|
|
|
### starpunk/config.py
|
|
|
|
**Location**: `/home/phil/Projects/starpunk/starpunk/config.py`
|
|
|
|
**Purpose**: Configuration management and validation.
|
|
|
|
**Contents**:
|
|
```python
|
|
"""
|
|
Configuration management for StarPunk
|
|
Loads settings from environment variables and .env file
|
|
"""
|
|
|
|
import os
|
|
from pathlib import Path
|
|
from dotenv import load_dotenv
|
|
|
|
|
|
def load_config(app, config_override=None):
|
|
"""
|
|
Load configuration into Flask app
|
|
|
|
Args:
|
|
app: Flask application instance
|
|
config_override: Optional dict to override config values
|
|
"""
|
|
# Load .env file
|
|
load_dotenv()
|
|
|
|
# Site configuration
|
|
app.config['SITE_URL'] = os.getenv('SITE_URL', 'http://localhost:5000')
|
|
app.config['SITE_NAME'] = os.getenv('SITE_NAME', 'StarPunk')
|
|
app.config['SITE_AUTHOR'] = os.getenv('SITE_AUTHOR', 'Unknown')
|
|
app.config['SITE_DESCRIPTION'] = os.getenv(
|
|
'SITE_DESCRIPTION',
|
|
'A minimal IndieWeb CMS'
|
|
)
|
|
|
|
# Authentication
|
|
app.config['ADMIN_ME'] = os.getenv('ADMIN_ME')
|
|
app.config['SESSION_SECRET'] = os.getenv('SESSION_SECRET')
|
|
app.config['SESSION_LIFETIME'] = int(os.getenv('SESSION_LIFETIME', '30'))
|
|
app.config['INDIELOGIN_URL'] = os.getenv(
|
|
'INDIELOGIN_URL',
|
|
'https://indielogin.com'
|
|
)
|
|
|
|
# Validate required configuration
|
|
if not app.config['SESSION_SECRET']:
|
|
raise ValueError(
|
|
"SESSION_SECRET must be set in .env file. "
|
|
"Generate with: python3 -c \"import secrets; print(secrets.token_hex(32))\""
|
|
)
|
|
|
|
# Flask secret key (uses SESSION_SECRET by default)
|
|
app.config['SECRET_KEY'] = os.getenv(
|
|
'FLASK_SECRET_KEY',
|
|
app.config['SESSION_SECRET']
|
|
)
|
|
|
|
# Data paths
|
|
app.config['DATA_PATH'] = Path(os.getenv('DATA_PATH', './data'))
|
|
app.config['NOTES_PATH'] = Path(os.getenv('NOTES_PATH', './data/notes'))
|
|
app.config['DATABASE_PATH'] = Path(
|
|
os.getenv('DATABASE_PATH', './data/starpunk.db')
|
|
)
|
|
|
|
# Flask environment
|
|
app.config['ENV'] = os.getenv('FLASK_ENV', 'development')
|
|
app.config['DEBUG'] = os.getenv('FLASK_DEBUG', '1') == '1'
|
|
|
|
# Logging
|
|
app.config['LOG_LEVEL'] = os.getenv('LOG_LEVEL', 'INFO')
|
|
|
|
# Apply overrides if provided
|
|
if config_override:
|
|
app.config.update(config_override)
|
|
|
|
# Ensure data directories exist
|
|
app.config['DATA_PATH'].mkdir(parents=True, exist_ok=True)
|
|
app.config['NOTES_PATH'].mkdir(parents=True, exist_ok=True)
|
|
```
|
|
|
|
---
|
|
|
|
### starpunk/database.py
|
|
|
|
**Location**: `/home/phil/Projects/starpunk/starpunk/database.py`
|
|
|
|
**Purpose**: Database initialization and schema management.
|
|
|
|
**Contents**:
|
|
```python
|
|
"""
|
|
Database initialization and operations for StarPunk
|
|
SQLite database for metadata, sessions, and tokens
|
|
"""
|
|
|
|
import sqlite3
|
|
from pathlib import Path
|
|
|
|
|
|
# Database schema
|
|
SCHEMA_SQL = """
|
|
-- Notes metadata (content is in files)
|
|
CREATE TABLE IF NOT EXISTS notes (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
slug TEXT UNIQUE NOT NULL,
|
|
file_path TEXT UNIQUE NOT NULL,
|
|
published BOOLEAN DEFAULT 0,
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
content_hash TEXT
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_notes_created_at ON notes(created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_notes_published ON notes(published);
|
|
CREATE INDEX IF NOT EXISTS idx_notes_slug ON notes(slug);
|
|
|
|
-- Authentication sessions (IndieLogin)
|
|
CREATE TABLE IF NOT EXISTS sessions (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
session_token TEXT UNIQUE NOT NULL,
|
|
me TEXT NOT NULL,
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
expires_at TIMESTAMP NOT NULL,
|
|
last_used_at TIMESTAMP
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_sessions_token ON sessions(session_token);
|
|
CREATE INDEX IF NOT EXISTS idx_sessions_expires ON sessions(expires_at);
|
|
|
|
-- Micropub access tokens
|
|
CREATE TABLE IF NOT EXISTS tokens (
|
|
token TEXT PRIMARY KEY,
|
|
me TEXT NOT NULL,
|
|
client_id TEXT,
|
|
scope TEXT,
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
expires_at TIMESTAMP
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_tokens_me ON tokens(me);
|
|
|
|
-- CSRF state tokens (for IndieAuth flow)
|
|
CREATE TABLE IF NOT EXISTS auth_state (
|
|
state TEXT PRIMARY KEY,
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
expires_at TIMESTAMP NOT NULL
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_auth_state_expires ON auth_state(expires_at);
|
|
"""
|
|
|
|
|
|
def init_db(app=None):
|
|
"""
|
|
Initialize database schema
|
|
|
|
Args:
|
|
app: Flask application instance (optional, for config access)
|
|
"""
|
|
if app:
|
|
db_path = app.config['DATABASE_PATH']
|
|
else:
|
|
# Fallback to default path
|
|
db_path = Path('./data/starpunk.db')
|
|
|
|
# Ensure parent directory exists
|
|
db_path.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
# Create database and schema
|
|
conn = sqlite3.connect(db_path)
|
|
try:
|
|
conn.executescript(SCHEMA_SQL)
|
|
conn.commit()
|
|
print(f"Database initialized: {db_path}")
|
|
finally:
|
|
conn.close()
|
|
|
|
|
|
def get_db(app):
|
|
"""
|
|
Get database connection
|
|
|
|
Args:
|
|
app: Flask application instance
|
|
|
|
Returns:
|
|
sqlite3.Connection
|
|
"""
|
|
db_path = app.config['DATABASE_PATH']
|
|
conn = sqlite3.connect(db_path)
|
|
conn.row_factory = sqlite3.Row # Return rows as dictionaries
|
|
return conn
|
|
```
|
|
|
|
---
|
|
|
|
### tests/conftest.py
|
|
|
|
**Location**: `/home/phil/Projects/starpunk/tests/conftest.py`
|
|
|
|
**Purpose**: Pytest configuration and shared fixtures.
|
|
|
|
**Contents**:
|
|
```python
|
|
"""
|
|
Pytest configuration and fixtures for StarPunk tests
|
|
"""
|
|
|
|
import pytest
|
|
import tempfile
|
|
from pathlib import Path
|
|
from starpunk import create_app
|
|
from starpunk.database import init_db
|
|
|
|
|
|
@pytest.fixture
|
|
def app():
|
|
"""Create test Flask application"""
|
|
# Create temporary directory for test data
|
|
temp_dir = tempfile.mkdtemp()
|
|
temp_path = Path(temp_dir)
|
|
|
|
# Test configuration
|
|
config = {
|
|
'TESTING': True,
|
|
'DEBUG': False,
|
|
'DATA_PATH': temp_path,
|
|
'NOTES_PATH': temp_path / 'notes',
|
|
'DATABASE_PATH': temp_path / 'test.db',
|
|
'SESSION_SECRET': 'test-secret-key',
|
|
'ADMIN_ME': 'https://test.example.com',
|
|
'SITE_URL': 'http://localhost:5000',
|
|
}
|
|
|
|
# Create app with test config
|
|
app = create_app(config)
|
|
|
|
yield app
|
|
|
|
# Cleanup (optional - temp dir will be cleaned up by OS)
|
|
|
|
|
|
@pytest.fixture
|
|
def client(app):
|
|
"""Create test client"""
|
|
return app.test_client()
|
|
|
|
|
|
@pytest.fixture
|
|
def runner(app):
|
|
"""Create test CLI runner"""
|
|
return app.test_cli_runner()
|
|
```
|
|
|
|
---
|
|
|
|
### LICENSE
|
|
|
|
**Location**: `/home/phil/Projects/starpunk/LICENSE`
|
|
|
|
**Purpose**: Project license (MIT recommended for IndieWeb projects).
|
|
|
|
**Contents**:
|
|
```
|
|
MIT License
|
|
|
|
Copyright (c) 2024 [Your Name]
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
SOFTWARE.
|
|
```
|
|
|
|
---
|
|
|
|
## Database Initialization
|
|
|
|
### Initial Schema
|
|
|
|
The database schema is defined in `starpunk/database.py` and includes:
|
|
|
|
**Tables**:
|
|
1. `notes` - Note metadata (8 columns)
|
|
2. `sessions` - Admin sessions (6 columns)
|
|
3. `tokens` - Micropub access tokens (6 columns)
|
|
4. `auth_state` - CSRF tokens (3 columns)
|
|
|
|
**Indexes**:
|
|
- `idx_notes_created_at` - For chronological ordering
|
|
- `idx_notes_published` - For filtering published notes
|
|
- `idx_notes_slug` - For slug lookups
|
|
- `idx_sessions_token` - For session validation
|
|
- `idx_sessions_expires` - For cleanup
|
|
- `idx_tokens_me` - For user token lookup
|
|
- `idx_auth_state_expires` - For cleanup
|
|
|
|
### Initialization Process
|
|
|
|
```python
|
|
# Automatic initialization on app creation
|
|
from starpunk import create_app
|
|
app = create_app() # Database initialized here
|
|
|
|
# Or manual initialization
|
|
from starpunk.database import init_db
|
|
init_db() # Creates data/starpunk.db with schema
|
|
```
|
|
|
|
---
|
|
|
|
## Bootstrap Checklist
|
|
|
|
When creating a new StarPunk project from scratch:
|
|
|
|
1. **Create root directory**
|
|
```bash
|
|
mkdir starpunk && cd starpunk
|
|
```
|
|
|
|
2. **Create configuration files**
|
|
- [ ] `.gitignore` (from this document)
|
|
- [ ] `.env.example` (from this document)
|
|
- [ ] `requirements.txt` (from this document)
|
|
- [ ] `requirements-dev.txt` (from this document)
|
|
- [ ] `README.md` (from this document)
|
|
- [ ] `LICENSE` (from this document)
|
|
|
|
3. **Create application files**
|
|
- [ ] `app.py` (from this document)
|
|
- [ ] `starpunk/__init__.py` (from this document)
|
|
- [ ] `starpunk/config.py` (from this document)
|
|
- [ ] `starpunk/database.py` (from this document)
|
|
|
|
4. **Create test files**
|
|
- [ ] `tests/__init__.py` (empty file)
|
|
- [ ] `tests/conftest.py` (from this document)
|
|
|
|
5. **Create directory structure**
|
|
```bash
|
|
mkdir -p starpunk static/css static/js templates/admin tests docs/architecture docs/decisions docs/design docs/standards
|
|
```
|
|
|
|
6. **Initialize git repository**
|
|
```bash
|
|
git init
|
|
git add .gitignore .env.example requirements.txt README.md LICENSE
|
|
git commit -m "Initial commit"
|
|
```
|
|
|
|
7. **Setup development environment**
|
|
```bash
|
|
uv venv .venv --python 3.11
|
|
uv pip install -r requirements.txt
|
|
cp .env.example .env
|
|
# Edit .env with your values
|
|
```
|
|
|
|
8. **Initialize database**
|
|
```bash
|
|
.venv/bin/python -c "from starpunk.database import init_db; init_db()"
|
|
```
|
|
|
|
9. **Verify setup**
|
|
```bash
|
|
.venv/bin/python -c "from starpunk import create_app; app = create_app(); print('✓ App created successfully')"
|
|
```
|
|
|
|
10. **Run development server**
|
|
```bash
|
|
.venv/bin/flask --app app.py run --debug
|
|
```
|
|
|
|
---
|
|
|
|
## File Creation Order
|
|
|
|
For developer agents, create files in this order to minimize errors:
|
|
|
|
1. `.gitignore` (prevent accidental commits)
|
|
2. `.env.example` (configuration template)
|
|
3. `requirements.txt` (dependencies first)
|
|
4. `README.md` (documentation)
|
|
5. `LICENSE` (legal)
|
|
6. `starpunk/__init__.py` (package structure)
|
|
7. `starpunk/config.py` (configuration loading)
|
|
8. `starpunk/database.py` (database setup)
|
|
9. `app.py` (application entry point)
|
|
10. `tests/conftest.py` (test infrastructure)
|
|
|
|
---
|
|
|
|
## Verification
|
|
|
|
After creating all initial files, verify:
|
|
|
|
```bash
|
|
# Check all files exist
|
|
test -f .gitignore && echo "✓ .gitignore"
|
|
test -f .env.example && echo "✓ .env.example"
|
|
test -f requirements.txt && echo "✓ requirements.txt"
|
|
test -f requirements-dev.txt && echo "✓ requirements-dev.txt"
|
|
test -f README.md && echo "✓ README.md"
|
|
test -f LICENSE && echo "✓ LICENSE"
|
|
test -f app.py && echo "✓ app.py"
|
|
test -f starpunk/__init__.py && echo "✓ starpunk/__init__.py"
|
|
test -f starpunk/config.py && echo "✓ starpunk/config.py"
|
|
test -f starpunk/database.py && echo "✓ starpunk/database.py"
|
|
test -f tests/conftest.py && echo "✓ tests/conftest.py"
|
|
|
|
# Verify Python syntax
|
|
python3 -m py_compile app.py starpunk/*.py tests/*.py
|
|
echo "✓ All Python files have valid syntax"
|
|
|
|
# Verify gitignore works
|
|
git check-ignore data/ .env .venv/
|
|
echo "✓ Sensitive files are gitignored"
|
|
```
|
|
|
|
---
|
|
|
|
## References
|
|
|
|
- [Project Structure Design](/home/phil/Projects/starpunk/docs/design/project-structure.md)
|
|
- [Development Setup Standards](/home/phil/Projects/starpunk/docs/standards/development-setup.md)
|
|
- [ADR-002: Flask Extensions](/home/phil/Projects/starpunk/docs/decisions/ADR-002-flask-extensions.md)
|
|
- [ADR-006: Python Virtual Environment](/home/phil/Projects/starpunk/docs/decisions/ADR-006-python-virtual-environment-uv.md)
|
|
- [Python Packaging Guide](https://packaging.python.org/)
|
|
- [Flask Configuration Handling](https://flask.palletsprojects.com/en/3.0.x/config/)
|