- ADR-033: Database migration redesign - ADR-034: Full-text search with FTS5 - ADR-035: Custom slugs in Micropub - ADR-036: IndieAuth token verification method - ADR-039: Micropub URL construction fix - Implementation plan and decisions - Architecture specifications - Validation reports for implementation and search UI 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
144 lines
5.3 KiB
Markdown
144 lines
5.3 KiB
Markdown
# ADR-039: Micropub URL Construction Fix
|
|
|
|
## Status
|
|
Accepted
|
|
|
|
## Context
|
|
After the v1.0.0 release, a bug was discovered in the Micropub implementation where the Location header returned after creating a post contains a double slash:
|
|
|
|
- **Expected**: `https://starpunk.thesatelliteoflove.com/notes/so-starpunk-v100-is-complete`
|
|
- **Actual**: `https://starpunk.thesatelliteoflove.com//notes/so-starpunk-v100-is-complete`
|
|
|
|
### Root Cause Analysis
|
|
The issue occurs due to a mismatch between how SITE_URL is stored and used:
|
|
|
|
1. **Configuration Storage** (`starpunk/config.py`):
|
|
- SITE_URL is normalized to always end with a trailing slash (lines 26, 92)
|
|
- This is required for IndieAuth/OAuth specs where root URLs must have trailing slashes
|
|
- Example: `https://starpunk.thesatelliteoflove.com/`
|
|
|
|
2. **URL Construction** (`starpunk/micropub.py`):
|
|
- Constructs URLs using: `f"{site_url}/notes/{note.slug}"` (lines 311, 381)
|
|
- This adds a leading slash to the path segment
|
|
- Results in: `https://starpunk.thesatelliteoflove.com/` + `/notes/...` = double slash
|
|
|
|
3. **Inconsistent Handling**:
|
|
- RSS feed module (`starpunk/feed.py`) correctly strips trailing slash before use (line 77)
|
|
- Micropub module doesn't handle this, causing the bug
|
|
|
|
## Decision
|
|
Fix the URL construction in the Micropub module by removing the leading slash from the path segment. This maintains the trailing slash convention in SITE_URL while ensuring correct URL construction.
|
|
|
|
### Implementation Approach
|
|
Change the URL construction pattern from:
|
|
```python
|
|
permalink = f"{site_url}/notes/{note.slug}"
|
|
```
|
|
|
|
To:
|
|
```python
|
|
permalink = f"{site_url}notes/{note.slug}"
|
|
```
|
|
|
|
This works because SITE_URL is guaranteed to have a trailing slash.
|
|
|
|
### Affected Code Locations
|
|
1. `starpunk/micropub.py` line 311 - Location header in `handle_create`
|
|
2. `starpunk/micropub.py` line 381 - URL in Microformats2 response in `handle_query`
|
|
|
|
## Rationale
|
|
|
|
### Why Not Strip the Trailing Slash?
|
|
We could follow the RSS feed approach and strip the trailing slash:
|
|
```python
|
|
site_url = site_url.rstrip("/")
|
|
permalink = f"{site_url}/notes/{note.slug}"
|
|
```
|
|
|
|
However, this approach has downsides:
|
|
- Adds unnecessary processing to every request
|
|
- Creates inconsistency with how SITE_URL is used elsewhere
|
|
- The trailing slash is intentionally added for IndieAuth compliance
|
|
|
|
### Why This Solution?
|
|
- **Minimal change**: Only modifies the string literal, not the logic
|
|
- **Consistent**: SITE_URL remains normalized with trailing slash throughout
|
|
- **Efficient**: No runtime string manipulation needed
|
|
- **Clear intent**: The code explicitly shows we expect SITE_URL to end with `/`
|
|
|
|
## Consequences
|
|
|
|
### Positive
|
|
- Fixes the immediate bug with minimal code changes
|
|
- No configuration changes required
|
|
- No database migrations needed
|
|
- Backward compatible - doesn't break existing data
|
|
- Fast to implement and test
|
|
|
|
### Negative
|
|
- Developers must remember that SITE_URL has a trailing slash
|
|
- Could be confusing without documentation
|
|
- Potential for similar bugs if pattern isn't followed elsewhere
|
|
|
|
### Mitigation
|
|
- Add a comment at each URL construction site explaining the trailing slash convention
|
|
- Consider adding a utility function in future versions for URL construction
|
|
- Document the SITE_URL trailing slash convention clearly
|
|
|
|
## Alternatives Considered
|
|
|
|
### 1. Strip Trailing Slash at Usage Site
|
|
```python
|
|
site_url = current_app.config.get("SITE_URL", "http://localhost:5000").rstrip("/")
|
|
permalink = f"{site_url}/notes/{note.slug}"
|
|
```
|
|
- **Pros**: More explicit, follows RSS feed pattern
|
|
- **Cons**: Extra processing, inconsistent with config intention
|
|
|
|
### 2. Remove Trailing Slash from Configuration
|
|
Modify `config.py` to not add trailing slashes to SITE_URL.
|
|
- **Pros**: Simpler URL construction
|
|
- **Cons**: Breaks IndieAuth spec compliance, requires migration for existing deployments
|
|
|
|
### 3. Create URL Builder Utility
|
|
```python
|
|
def build_url(base, *segments):
|
|
"""Build URL from base and path segments"""
|
|
return "/".join([base.rstrip("/")] + list(segments))
|
|
```
|
|
- **Pros**: Centralized URL construction, prevents future bugs
|
|
- **Cons**: Over-engineering for a simple fix, adds unnecessary abstraction for v1.0.1
|
|
|
|
### 4. Use urllib.parse.urljoin
|
|
```python
|
|
from urllib.parse import urljoin
|
|
permalink = urljoin(site_url, f"notes/{note.slug}")
|
|
```
|
|
- **Pros**: Standard library solution, handles edge cases
|
|
- **Cons**: Adds import, slightly less readable, overkill for this use case
|
|
|
|
## Implementation Notes
|
|
|
|
### Version Impact
|
|
- Current version: v1.0.0
|
|
- Fix version: v1.0.1 (PATCH increment - backward-compatible bug fix)
|
|
|
|
### Testing Requirements
|
|
1. Verify Location header has single slash
|
|
2. Test with various SITE_URL configurations (with/without trailing slash)
|
|
3. Ensure RSS feed still works correctly
|
|
4. Check all other URL constructions in the codebase
|
|
|
|
### Release Type
|
|
This qualifies as a **hotfix** because:
|
|
- It fixes a bug in production (v1.0.0)
|
|
- The fix is isolated and low-risk
|
|
- No new features or breaking changes
|
|
- Critical for proper Micropub client operation
|
|
|
|
## References
|
|
- [Issue Report]: Malformed redirect URL in Micropub implementation
|
|
- [W3C Micropub Spec](https://www.w3.org/TR/micropub/): Location header requirements
|
|
- [IndieAuth Spec](https://indieauth.spec.indieweb.org/): Client ID URL requirements
|
|
- ADR-028: Micropub Implementation Strategy
|
|
- docs/standards/versioning-strategy.md: Version increment guidelines |