Skip to content

Commit 9519e20

Browse files
committed
Release v1.2.0: Background jobs, code quality tools, and API documentation
Major Features: - Background job system with database-backed queue for asynchronous feed fetching - Static analysis (PHPStan) and code style enforcement (PHP-CS-Fixer) - Complete OpenAPI/Swagger API documentation - Response migration completed (100% standardized) New Components: - JobQueue and Worker classes for background processing - FeedCleanupService with configurable retention policies - Worker CLI script for processing jobs - Comprehensive API documentation (OpenAPI 3.0 + markdown) Code Quality: - All 27 source files formatted to PSR-12 standards - PHPStan static analysis at level 5 (0 errors) - Enhanced type safety and error handling Documentation: - BACKGROUND_JOBS.md - Job system setup and usage - API_DOCUMENTATION.md - Complete API reference - CODE_QUALITY.md - Tool usage guide - ENV_CONFIGURATION.md - Environment variable reference Configuration: - Added PHPStan and PHP-CS-Fixer as dev dependencies - Docker Compose updated with new volume mounts - New environment variables for job system and feed retention Files: 28 modified, 9 new files, 2 new directories
1 parent 420c61d commit 9519e20

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+3006
-680
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,5 @@ coverage/
1010
logs/
1111
*.log
1212
data/cache/
13+
.php-cs-fixer.cache
14+
.phpstan/

.php-cs-fixer.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
$finder = PhpCsFixer\Finder::create()
4+
->in(__DIR__)
5+
->exclude('vendor')
6+
->exclude('data')
7+
->exclude('coverage')
8+
->exclude('node_modules')
9+
->name('*.php');
10+
11+
$config = new PhpCsFixer\Config();
12+
return $config
13+
->setRules([
14+
'@PSR12' => true,
15+
'array_syntax' => ['syntax' => 'short'],
16+
'ordered_imports' => ['sort_algorithm' => 'alpha'],
17+
'no_unused_imports' => true,
18+
'not_operator_with_successor_space' => true,
19+
'trailing_comma_in_multiline' => true,
20+
'phpdoc_scalar' => true,
21+
'unary_operator_spaces' => true,
22+
'binary_operator_spaces' => true,
23+
'blank_line_before_statement' => [
24+
'statements' => ['break', 'continue', 'declare', 'return', 'throw', 'try'],
25+
],
26+
'phpdoc_single_line_var_spacing' => true,
27+
'phpdoc_var_without_name' => true,
28+
'class_attributes_separation' => [
29+
'elements' => [
30+
'method' => 'one',
31+
],
32+
],
33+
'method_argument_space' => [
34+
'on_multiline' => 'ensure_fully_multiline',
35+
'keep_multiple_spaces_after_comma' => true,
36+
],
37+
])
38+
->setFinder($finder)
39+
->setIndent(' ')
40+
->setLineEnding("\n");

API_DOCUMENTATION.md

Lines changed: 345 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,345 @@
1+
# API Documentation
2+
3+
Complete API reference for VibeReader's RESTful endpoints.
4+
5+
## Base URL
6+
7+
- **Development**: `http://localhost:9999`
8+
- **Production**: Your domain URL
9+
10+
## Authentication
11+
12+
All API endpoints (except `/api/version`) require authentication via session cookies. The session is established through the web interface login.
13+
14+
### CSRF Protection
15+
16+
State-changing operations (POST, PUT, DELETE) require a CSRF token. Include it in one of these ways:
17+
18+
1. **Header** (recommended for AJAX):
19+
```
20+
X-CSRF-Token: your-token-here
21+
```
22+
23+
2. **POST body** (for form submissions):
24+
```
25+
_token: your-token-here
26+
```
27+
28+
3. **Query parameter** (GET requests):
29+
```
30+
?_token=your-token-here
31+
```
32+
33+
Get the CSRF token from the HTML page (hidden input field) or via JavaScript:
34+
```javascript
35+
const token = document.querySelector('input[name="_token"]')?.value;
36+
```
37+
38+
## Response Format
39+
40+
### Success Response
41+
42+
```json
43+
{
44+
"success": true,
45+
"data": { ... },
46+
"message": "Optional message"
47+
}
48+
```
49+
50+
### Error Response
51+
52+
```json
53+
{
54+
"success": false,
55+
"error": "Error message",
56+
"errors": ["Optional array of detailed errors"]
57+
}
58+
```
59+
60+
## Endpoints
61+
62+
### Feeds
63+
64+
#### GET /api/feeds
65+
66+
Get all feeds for the current user.
67+
68+
**Response:** Array of feed objects
69+
```json
70+
[
71+
{
72+
"id": 1,
73+
"title": "Example Feed",
74+
"url": "https://example.com/feed.xml",
75+
"feed_type": "rss",
76+
"item_count": 25,
77+
"unread_count": 5,
78+
"folder_id": 1,
79+
"folder_name": "News",
80+
"last_fetched": "2024-01-19T12:00:00Z"
81+
}
82+
]
83+
```
84+
85+
#### GET /api/feeds/{feedId}/items
86+
87+
Get items for a specific feed.
88+
89+
**Parameters:**
90+
- `feedId` (path, integer) - Feed ID
91+
92+
**Response:** Array of feed item objects
93+
```json
94+
[
95+
{
96+
"id": 123,
97+
"feed_id": 1,
98+
"title": "Article Title",
99+
"link": "https://example.com/article",
100+
"content": "<p>Article content...</p>",
101+
"summary": "Article summary",
102+
"author": "Author Name",
103+
"published_at": "2024-01-19T10:00:00Z",
104+
"is_read": 0
105+
}
106+
]
107+
```
108+
109+
**Note:** Returns only unread items if user has "hide read items" preference enabled.
110+
111+
### Items
112+
113+
#### GET /api/items/{itemId}
114+
115+
Get a single feed item by ID.
116+
117+
**Parameters:**
118+
- `itemId` (path, integer) - Item ID
119+
120+
**Response:** Feed item object with feed title
121+
```json
122+
{
123+
"id": 123,
124+
"feed_id": 1,
125+
"feed_title": "Example Feed",
126+
"title": "Article Title",
127+
"link": "https://example.com/article",
128+
"content": "<p>Article content...</p>",
129+
"summary": "Article summary",
130+
"author": "Author Name",
131+
"published_at": "2024-01-19T10:00:00Z",
132+
"is_read": 0
133+
}
134+
```
135+
136+
#### POST /api/items/{itemId}/read
137+
138+
Mark an item as read.
139+
140+
**Parameters:**
141+
- `itemId` (path, integer) - Item ID
142+
143+
**Headers:**
144+
- `X-CSRF-Token` (required)
145+
146+
**Response:**
147+
```json
148+
{
149+
"success": true
150+
}
151+
```
152+
153+
### Search
154+
155+
#### GET /api/search?q={query}
156+
157+
Search feed items across all user feeds.
158+
159+
**Query Parameters:**
160+
- `q` (required, string) - Search query
161+
162+
**Response:** Array of matching feed items (up to 100 results)
163+
```json
164+
[
165+
{
166+
"id": 123,
167+
"feed_id": 1,
168+
"feed_title": "Example Feed",
169+
"title": "Article Title",
170+
"link": "https://example.com/article",
171+
"content": "Article content...",
172+
"summary": "Article summary",
173+
"author": "Author Name",
174+
"published_at": "2024-01-19T10:00:00Z",
175+
"is_read": 0
176+
}
177+
]
178+
```
179+
180+
**Searches in:** title, content, summary, author fields
181+
182+
### System
183+
184+
#### GET /api/version
185+
186+
Get application version information.
187+
188+
**Authentication:** Not required
189+
190+
**Response:**
191+
```json
192+
{
193+
"app_name": "VibeReader",
194+
"version": "1.1.1",
195+
"version_string": "VibeReader/1.1.1"
196+
}
197+
```
198+
199+
### Jobs
200+
201+
#### GET /api/jobs/stats
202+
203+
Get background job queue statistics.
204+
205+
**Response:**
206+
```json
207+
{
208+
"pending": 5,
209+
"processing": 1,
210+
"completed": 150,
211+
"failed": 2
212+
}
213+
```
214+
215+
#### POST /api/jobs/cleanup
216+
217+
Queue a cleanup job for feed items.
218+
219+
**Headers:**
220+
- `X-CSRF-Token` (required)
221+
222+
**Body (application/x-www-form-urlencoded):**
223+
- `feed_id` (optional, integer) - Specific feed ID to clean (null for all)
224+
- `retention_days` (optional, integer) - Days to keep items
225+
- `retention_count` (optional, integer) - Max items per feed
226+
227+
**Response:**
228+
```json
229+
{
230+
"success": true,
231+
"data": {
232+
"job_id": 456,
233+
"message": "Cleanup job queued"
234+
}
235+
}
236+
```
237+
238+
## Data Types
239+
240+
### Date Format
241+
242+
All dates are returned in ISO 8601 format with UTC timezone:
243+
```
244+
2024-01-19T12:00:00Z
245+
```
246+
247+
### Read Status
248+
249+
Items use integer values for read status:
250+
- `0` = Unread
251+
- `1` = Read
252+
253+
## Error Codes
254+
255+
- `200` - Success
256+
- `400` - Bad Request (invalid parameters)
257+
- `401` - Unauthorized (authentication required)
258+
- `403` - Forbidden (CSRF token invalid or insufficient permissions)
259+
- `404` - Not Found
260+
- `429` - Too Many Requests (rate limited)
261+
- `500` - Internal Server Error
262+
263+
## OpenAPI Specification
264+
265+
A complete OpenAPI 3.0 specification is available in `openapi.yaml`.
266+
267+
### Viewing the API Documentation
268+
269+
You can use tools like:
270+
- [Swagger UI](https://swagger.io/tools/swagger-ui/)
271+
- [ReDoc](https://redocly.com/)
272+
- [Postman](https://www.postman.com/) (import openapi.yaml)
273+
274+
### Example: Using Swagger UI
275+
276+
1. Install Swagger UI:
277+
```bash
278+
npm install -g swagger-ui-serve
279+
```
280+
281+
2. Serve the OpenAPI spec:
282+
```bash
283+
swagger-ui-serve openapi.yaml
284+
```
285+
286+
3. Open `http://localhost:3000` in your browser
287+
288+
## Examples
289+
290+
### JavaScript/Fetch
291+
292+
```javascript
293+
// Get feeds
294+
const feeds = await fetch('/api/feeds', {
295+
credentials: 'include' // Include session cookie
296+
}).then(r => r.json());
297+
298+
// Mark item as read
299+
const token = document.querySelector('input[name="_token"]').value;
300+
await fetch('/api/items/123/read', {
301+
method: 'POST',
302+
headers: {
303+
'X-CSRF-Token': token
304+
},
305+
credentials: 'include'
306+
});
307+
308+
// Search
309+
const results = await fetch('/api/search?q=php', {
310+
credentials: 'include'
311+
}).then(r => r.json());
312+
```
313+
314+
### cURL
315+
316+
```bash
317+
# Get feeds (with session cookie)
318+
curl -b cookies.txt http://localhost:9999/api/feeds
319+
320+
# Mark item as read (with CSRF token)
321+
curl -X POST \
322+
-H "X-CSRF-Token: your-token" \
323+
-b cookies.txt \
324+
http://localhost:9999/api/items/123/read
325+
326+
# Search
327+
curl -b cookies.txt "http://localhost:9999/api/search?q=php"
328+
```
329+
330+
## Rate Limiting
331+
332+
Some endpoints have rate limiting enabled:
333+
- Login: 5 attempts per 15 minutes
334+
- API endpoints: 100 requests per minute
335+
336+
Rate limit information is available in response headers (if implemented).
337+
338+
## Notes
339+
340+
- All endpoints return JSON
341+
- Dates are in ISO 8601 UTC format
342+
- Authentication is session-based (cookie)
343+
- CSRF tokens are required for POST/PUT/DELETE operations
344+
- Search is case-insensitive
345+
- Item lists respect user preferences (hide read items, sort order)

0 commit comments

Comments
 (0)