## 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>
565 lines
11 KiB
Markdown
565 lines
11 KiB
Markdown
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
1. **Explicit Configuration**: Requires `DEV_MODE=true`
|
|
2. **Separate Routes**: `/dev/login` (not `/admin/login`)
|
|
3. **Route Protection**: Returns 404 if DEV_MODE=false
|
|
4. **Config Validation**: Prevents DEV_MODE + production URL
|
|
5. **Visual Warnings**: Red banner when dev mode active
|
|
6. **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):
|
|
```html
|
|
<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):
|
|
```html
|
|
<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
|
|
|
|
```python
|
|
# 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
|
|
|
|
```css
|
|
: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
|
|
|
|
```css
|
|
/* 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):
|
|
```python
|
|
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):
|
|
```python
|
|
from starpunk.notes import (
|
|
get_all_notes,
|
|
get_note_by_slug,
|
|
create_note,
|
|
update_note,
|
|
delete_note
|
|
)
|
|
```
|
|
|
|
**database.py** (Phase 1):
|
|
```python
|
|
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
|
|
|
|
```python
|
|
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
|
|
|
|
```python
|
|
@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
|
|
|
|
```python
|
|
# 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
|
|
|
|
1. Check `DEV_MODE=true` in `.env`
|
|
2. Check `DEV_ADMIN_ME` is set
|
|
3. Restart Flask server
|
|
4. Check logs for warnings
|
|
|
|
### Templates Not Found
|
|
|
|
1. Check templates/ directory exists
|
|
2. Check template paths in render_template()
|
|
3. Restart Flask server
|
|
|
|
### CSS Not Loading
|
|
|
|
1. Check static/css/style.css exists
|
|
2. Check url_for('static', filename='css/style.css')
|
|
3. Clear browser cache
|
|
|
|
### Authentication Not Working
|
|
|
|
1. Check ADMIN_ME is set correctly
|
|
2. Check SESSION_SECRET is set
|
|
3. Check IndieLogin callback URL matches
|
|
4. 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/micropub` endpoint
|
|
- 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
|
|
|
|
```bash
|
|
# 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
|