## Phase 4: Web Interface Implementation Implemented complete web interface with public and admin routes, templates, CSS, and development authentication. ### Core Features **Public Routes**: - Homepage with recent published notes - Note permalinks with microformats2 - Server-side rendering (Jinja2) **Admin Routes**: - Login via IndieLogin - Dashboard with note management - Create, edit, delete notes - Protected with @require_auth decorator **Development Authentication**: - Dev login bypass for local testing (DEV_MODE only) - Security safeguards per ADR-011 - Returns 404 when disabled **Templates & Frontend**: - Base layouts (public + admin) - 8 HTML templates with microformats2 - Custom responsive CSS (114 lines) - Error pages (404, 500) ### Bugfixes (v0.5.1 → v0.5.2) 1. **Cookie collision fix (v0.5.1)**: - Renamed auth cookie from "session" to "starpunk_session" - Fixed redirect loop between dev login and admin dashboard - Flask's session cookie no longer conflicts with auth 2. **HTTP 404 error handling (v0.5.1)**: - Update route now returns 404 for nonexistent notes - Delete route now returns 404 for nonexistent notes - Follows ADR-012 HTTP Error Handling Policy - Pattern consistency across all admin routes 3. **Note model enhancement (v0.5.2)**: - Exposed deleted_at field from database schema - Enables soft deletion verification in tests - Follows ADR-013 transparency principle ### Architecture **New ADRs**: - ADR-011: Development Authentication Mechanism - ADR-012: HTTP Error Handling Policy - ADR-013: Expose deleted_at Field in Note Model **Standards Compliance**: - Uses uv for Python environment - Black formatted, Flake8 clean - Follows git branching strategy - Version incremented per versioning strategy ### Test Results - 405/406 tests passing (99.75%) - 87% code coverage - All security tests passing - Manual testing confirmed working ### Documentation - Complete implementation reports in docs/reports/ - Architecture reviews in docs/reviews/ - Design documents in docs/design/ - CHANGELOG updated for v0.5.2 ### Files Changed **New Modules**: - starpunk/dev_auth.py - starpunk/routes/ (public, admin, auth, dev_auth) **Templates**: 10 files (base, pages, admin, errors) **Static**: CSS and optional JavaScript **Tests**: 4 test files for routes and templates **Docs**: 20+ architectural and implementation documents 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
11 KiB
11 KiB
Phase 4: Quick Reference
Phase: Web Interface Version: 0.5.0 Status: Design Complete Dependencies: Phase 3 (Authentication) ✓
Critical Decision: Development Authentication
Question: Should we implement a dev auth mechanism for local testing?
Answer: ✓ YES - Implement with strict safeguards
Why: Enable local testing without deploying to IndieLogin.com
How: Separate /dev/login route that only works when DEV_MODE=true
Safety: Returns 404 when disabled, visual warnings, config validation
Details: See ADR-011
What Phase 4 Delivers
Public Interface
- Homepage with recent notes (
/) - Note permalinks (
/note/<slug>) - Microformats2 markup (h-feed, h-entry)
Admin Interface
- Login via IndieLogin (
/admin/login) - Dashboard with note list (
/admin) - Create notes (
/admin/new) - Edit notes (
/admin/edit/<id>) - Delete notes (
/admin/delete/<id>)
Development Tools
- Dev auth for local testing (
/dev/login) - Configuration validation
- Dev mode warnings
Routes Summary
Public (No Auth)
GET / Homepage (note list)
GET /note/<slug> Note permalink
Auth Flow
GET /admin/login Login form
POST /admin/login Start IndieLogin flow
GET /auth/callback IndieLogin callback
POST /admin/logout Logout
Admin (Auth Required)
GET /admin Dashboard
GET /admin/new Create note form
POST /admin/new Save new note
GET /admin/edit/<id> Edit note form
POST /admin/edit/<id> Update note
POST /admin/delete/<id> Delete note
Dev (DEV_MODE Only)
GET /dev/login Instant login (bypasses IndieLogin)
File Structure
New Files (~2,770 lines total)
starpunk/routes/ # Route handlers
├── public.py # Public routes
├── admin.py # Admin routes
├── auth.py # Auth routes
└── dev_auth.py # Dev routes
starpunk/dev_auth.py # Dev auth module
templates/ # Jinja2 templates
├── base.html
├── index.html
├── note.html
└── admin/
├── base.html
├── login.html
├── dashboard.html
├── new.html
└── edit.html
static/css/style.css # ~350 lines
static/js/preview.js # Optional markdown preview
tests/
├── test_routes_public.py
├── test_routes_admin.py
└── test_dev_auth.py
Modified Files
starpunk/config.py # Add DEV_MODE, DEV_ADMIN_ME, VERSION
app.py # Register routes, validate config
CHANGELOG.md # Add v0.5.0 entry
Configuration
New Environment Variables
# Development Mode (default: false)
DEV_MODE=false # Set to 'true' for local dev
DEV_ADMIN_ME= # Your identity URL for dev mode
# Version (for display)
VERSION=0.5.0
Development Setup
# For local development
DEV_MODE=true
DEV_ADMIN_ME=https://yoursite.com
# For production (or leave unset)
DEV_MODE=false
ADMIN_ME=https://yoursite.com
Security Measures
Dev Auth Safeguards
- Explicit Configuration: Requires
DEV_MODE=true - Separate Routes:
/dev/login(not/admin/login) - Route Protection: Returns 404 if DEV_MODE=false
- Config Validation: Prevents DEV_MODE + production URL
- Visual Warnings: Red banner when dev mode active
- Logging: All dev auth logged with warnings
Production Security
- All admin routes use
@require_auth - HttpOnly, Secure, SameSite cookies
- CSRF state tokens
- Session expiry (30 days)
- Jinja2 auto-escaping (XSS prevention)
Template Architecture
Microformats
Homepage (h-feed):
<div class="h-feed">
<article class="h-entry">
<div class="e-content">...</div>
<time class="dt-published">...</time>
<a class="u-url" href="...">permalink</a>
</article>
</div>
Note Page (h-entry):
<article class="h-entry">
<div class="e-content">{{ note.html|safe }}</div>
<a class="u-url" href="{{ url_for('public.note', slug=note.slug) }}">
<time class="dt-published" datetime="{{ note.created_at.isoformat() }}">
{{ note.created_at.strftime('%B %d, %Y') }}
</time>
</a>
</article>
Flash Messages
# In routes
flash('Note created successfully', 'success')
flash('Error: Note not found', 'error')
# In templates
{% with messages = get_flashed_messages(with_categories=true) %}
{% for category, message in messages %}
<div class="flash flash-{{ category }}">{{ message }}</div>
{% endfor %}
{% endwith %}
CSS Architecture
Variables
:root {
/* Colors */
--color-text: #333;
--color-bg: #fff;
--color-link: #0066cc;
--color-success: #28a745;
--color-error: #dc3545;
--color-warning: #ffc107;
/* Typography */
--font-body: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
--font-mono: 'SF Mono', Monaco, monospace;
/* Spacing */
--spacing-md: 1rem;
--spacing-lg: 2rem;
/* Layout */
--max-width: 42rem;
}
Mobile-First
/* Base: Mobile */
body { padding: 1rem; }
/* Tablet and up */
@media (min-width: 768px) {
body { padding: 2rem; }
}
Testing Strategy
Coverage Target: >90%
Unit Tests
- Public routes (homepage, note permalink)
- Admin routes (dashboard, create, edit, delete)
- Dev auth (login, validation, route protection)
Integration Tests
- Full auth flow (mocked IndieLogin)
- Create note end-to-end
- Edit note end-to-end
- Delete note end-to-end
Manual Tests
- Browser testing (Chrome, Firefox, Safari)
- Mobile responsive
- Microformats validation (indiewebify.me)
- HTML5 validation (W3C)
- Real IndieLogin authentication
Implementation Checklist
Phase 4.1: Routes (8 hours)
- Create routes package
- Implement public routes
- Implement auth routes
- Implement admin routes
Phase 4.2: Templates (6 hours)
- Base templates
- Public templates
- Admin templates
Phase 4.3: Dev Auth (4 hours)
- dev_auth.py module
- Config validation
- Visual warnings
Phase 4.4: CSS (4 hours)
- style.css
- Responsive design
Phase 4.5: JS (Optional, 2 hours)
- preview.js
- Progressive enhancement
Phase 4.6: Testing (8 hours)
- Route tests
- Integration tests
- >90% coverage
Phase 4.7: Documentation (2 hours)
- Update CHANGELOG
- Document routes
- Version to 0.5.0
Total: ~34 hours
Acceptance Criteria
Must Pass
- All routes work correctly
- Authentication enforced on admin routes
- Dev auth blocked when DEV_MODE=false
- Templates render with microformats
- Flash messages work
- Test coverage >90%
- No security vulnerabilities
- Dev mode warnings display
- Mobile responsive
Performance Targets
- Homepage: < 200ms
- Note page: < 200ms
- Admin pages: < 200ms
- Form submit: < 100ms
Key Integrations
With Existing Modules
auth.py (Phase 3):
from starpunk.auth import require_auth, verify_session, destroy_session
@require_auth
def dashboard():
# User info in g.user_me
pass
notes.py (Phase 2):
from starpunk.notes import (
get_all_notes,
get_note_by_slug,
create_note,
update_note,
delete_note
)
database.py (Phase 1):
from starpunk.database import get_db
Risk Mitigation
Dev Auth Accidentally Enabled
Risk: Critical Mitigation:
- Config validation
- Startup warnings
- Visual indicators
- Deployment checklist
- Documentation
XSS Vulnerabilities
Risk: High Mitigation:
- Jinja2 auto-escaping
- No user HTML
- Code review
- Security testing
Session Theft
Risk: Medium Mitigation:
- HttpOnly cookies
- Secure flag (production)
- SameSite=Lax
- HTTPS required
Common Patterns
Protected Route
from starpunk.auth import require_auth
@app.route('/admin/dashboard')
@require_auth
def dashboard():
# g.user_me is set by require_auth
notes = get_all_notes()
return render_template('admin/dashboard.html', notes=notes)
Creating a Note
@app.route('/admin/new', methods=['POST'])
@require_auth
def create_note_submit():
content = request.form.get('content')
published = 'published' in request.form
try:
note = create_note(content, published)
flash(f'Note created: {note.slug}', 'success')
return redirect(url_for('admin.dashboard'))
except ValueError as e:
flash(f'Error: {e}', 'error')
return redirect(url_for('admin.new_note_form'))
Dev Mode Check
# In dev_auth.py
def dev_login():
if not current_app.config.get('DEV_MODE'):
abort(404) # Route doesn't exist
me = current_app.config.get('DEV_ADMIN_ME')
session_token = create_session(me)
current_app.logger.warning(
f"DEV MODE: Session created for {me} without authentication"
)
# Set cookie and redirect
response = redirect(url_for('admin.dashboard'))
response.set_cookie('session', session_token, httponly=True)
return response
Troubleshooting
Dev Auth Not Working
- Check
DEV_MODE=truein.env - Check
DEV_ADMIN_MEis set - Restart Flask server
- Check logs for warnings
Templates Not Found
- Check templates/ directory exists
- Check template paths in render_template()
- Restart Flask server
CSS Not Loading
- Check static/css/style.css exists
- Check url_for('static', filename='css/style.css')
- Clear browser cache
Authentication Not Working
- Check ADMIN_ME is set correctly
- Check SESSION_SECRET is set
- Check IndieLogin callback URL matches
- Check browser cookies enabled
Next Steps After Phase 4
Phase 5: RSS Feed
- Generate
/feed.xml - Valid RSS 2.0
- Published notes only
Phase 6: Micropub
/api/micropubendpoint- Accept h-entry posts
- IndieAuth token verification
V1.0.0
- Complete V1 features
- Security audit
- Performance optimization
- Production deployment
Documentation References
- ADR-011: Development Auth Decision
- Phase 4 Design: Complete specification
- Assessment Report: Architectural review
- Phase 3 Report: Authentication implementation
- ADR-003: Frontend Technology
- ADR-005: IndieLogin Authentication
- ADR-010: Authentication Module Design
Git Workflow
# Create feature branch
git checkout -b feature/phase-4-web-interface main
# Implement, test, commit frequently
git commit -m "Add public routes"
git commit -m "Add admin routes"
git commit -m "Add templates"
git commit -m "Add dev auth"
git commit -m "Add CSS"
git commit -m "Add tests"
# Update version
# Edit starpunk/__init__.py: __version__ = "0.5.0"
# Edit CHANGELOG.md
git commit -m "Bump version to 0.5.0"
# Merge to main
git checkout main
git merge feature/phase-4-web-interface
# Tag
git tag -a v0.5.0 -m "Release 0.5.0: Web Interface complete"
# Push
git push origin main v0.5.0
Status: Ready for Implementation Estimated Effort: 34 hours Target Version: 0.5.0 Developer: Use with Phase 4 Design Document