Skip to content

Commit b627839

Browse files
committed
docs(website): scaffold Docusaurus documentation site
Add Docusaurus-based documentation site under website/ with custom landing page, branding, showcase page, and ~34 markdown pages covering API reference, concepts, configuration, guides, and community docs.
1 parent 3a9ef07 commit b627839

51 files changed

Lines changed: 25612 additions & 0 deletions

Some content is hidden

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

website/.gitignore

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Dependencies
2+
/node_modules
3+
4+
# Production
5+
/build
6+
7+
# Generated files
8+
.docusaurus
9+
.cache-loader
10+
11+
# Misc
12+
.DS_Store
13+
.env.local
14+
.env.development.local
15+
.env.test.local
16+
.env.production.local
17+
18+
npm-debug.log*
19+
yarn-debug.log*
20+
yarn-error.log*

website/README.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Website
2+
3+
This website is built using [Docusaurus](https://docusaurus.io/), a modern static website generator.
4+
5+
## Installation
6+
7+
```bash
8+
yarn
9+
```
10+
11+
## Local Development
12+
13+
```bash
14+
yarn start
15+
```
16+
17+
This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
18+
19+
## Build
20+
21+
```bash
22+
yarn build
23+
```
24+
25+
This command generates static content into the `build` directory and can be served using any static contents hosting service.
26+
27+
## Deployment
28+
29+
Using SSH:
30+
31+
```bash
32+
USE_SSH=true yarn deploy
33+
```
34+
35+
Not using SSH:
36+
37+
```bash
38+
GIT_USER=<Your GitHub username> yarn deploy
39+
```
40+
41+
If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.

website/docs/api/authentication.md

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
---
2+
id: authentication
3+
title: Authentication
4+
description: API keys, session cookies, and admin authentication.
5+
---
6+
7+
# Authentication
8+
9+
PiazzaViva uses three authentication mechanisms, each suited to a different audience.
10+
11+
## API key (Public API)
12+
13+
Every request to `/api/public/*` must include:
14+
15+
```http
16+
X-API-Key: your_key_here
17+
```
18+
19+
### Getting a key
20+
21+
Email `partners@piazzaviva.it` with:
22+
23+
- Your name / organization
24+
- Intended use case
25+
- Expected volume
26+
27+
For self-hosted instances, add keys to `.env`:
28+
29+
```env
30+
API_KEYS="partner-a,partner-b,partner-c"
31+
```
32+
33+
Keys are validated against this comma-separated list. There is no hash storage — treat the env var as a secret.
34+
35+
### Example
36+
37+
```bash
38+
curl -H "X-API-Key: dev-key-123" \
39+
https://piazzaviva.it/api/public/events
40+
```
41+
42+
Missing or invalid keys yield:
43+
44+
```json
45+
{ "error": "Unauthorized", "code": "INVALID_API_KEY" }
46+
```
47+
48+
…with HTTP **401**.
49+
50+
## Session cookie (internal)
51+
52+
The PiazzaViva web app authenticates with a session cookie issued by `/api/auth/login`:
53+
54+
```http
55+
Cookie: pv_session=<opaque>; HttpOnly; SameSite=Lax; Secure
56+
```
57+
58+
Sessions:
59+
60+
- Live 30 days, sliding on each request.
61+
- Store in the `Session` table — server-side revocable.
62+
- Are not JWTs. There is no client-side token to leak.
63+
64+
Internal routes also require `X-CSRF-Token` on state-changing methods (`POST`, `PATCH`, `DELETE`). See [Security](../concepts/security.md).
65+
66+
## Admin API key
67+
68+
`/api/admin/*` endpoints check:
69+
70+
```http
71+
X-API-Key: $ADMIN_API_KEY
72+
```
73+
74+
`ADMIN_API_KEY` is a single env-var secret. Rotate it by changing the env and redeploying.
75+
76+
## Webhook signatures
77+
78+
`/api/webhooks/stripe` verifies the `Stripe-Signature` header using `STRIPE_WEBHOOK_SECRET` and `stripe.webhooks.constructEvent`. Other webhooks (future integrations) use the same pattern: shared-secret HMAC over the raw body.
79+
80+
## Decision table
81+
82+
| Calling from | Use |
83+
|---|---|
84+
| Third-party server | API key |
85+
| Mobile app you control | API key |
86+
| The PiazzaViva web UI | Session cookie (automatic) |
87+
| Internal ops script | Admin API key |
88+
| Stripe / providers | Signed webhooks |

website/docs/api/errors.md

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
---
2+
id: errors
3+
title: Error reference
4+
description: Every error code PiazzaViva returns, with what to do about it.
5+
---
6+
7+
# Error reference
8+
9+
All API errors share a single JSON shape:
10+
11+
```json
12+
{ "error": "human-readable message", "code": "MACHINE_CODE" }
13+
```
14+
15+
`error` is for humans; `code` is the stable identifier you should branch on in code.
16+
17+
## Status code mapping
18+
19+
| HTTP | Used for |
20+
|---|---|
21+
| `400` | Validation / business-rule failure |
22+
| `401` | Missing or invalid authentication |
23+
| `403` | Authenticated but not authorized |
24+
| `404` | Resource doesn't exist or isn't visible to the caller |
25+
| `409` | State conflict (idempotency, dup creation, already-done action) |
26+
| `422` | Well-formed but semantically invalid |
27+
| `429` | Rate limited |
28+
| `500` | Bug. Please report. |
29+
| `503` | Dependency outage (Stripe/OpenAI/Resend down) |
30+
31+
## Error codes
32+
33+
### Authentication
34+
35+
| Code | HTTP | Remedy |
36+
|---|---|---|
37+
| `MISSING_API_KEY` | 401 | Add `X-API-Key` header |
38+
| `INVALID_API_KEY` | 401 | Verify the key matches `API_KEYS` |
39+
| `UNAUTHENTICATED` | 401 | Log in and retry |
40+
| `CSRF_INVALID` | 403 | Fetch a fresh CSRF token |
41+
| `FORBIDDEN` | 403 | You don't have the role for this action |
42+
43+
### Validation
44+
45+
| Code | HTTP | Remedy |
46+
|---|---|---|
47+
| `VALIDATION_FAILED` | 400 | See `details[]` for per-field errors |
48+
| `INVALID_DATE` | 400 | Use ISO-8601 |
49+
| `INVALID_CATEGORY` | 400 | Use one of the documented slugs |
50+
| `INVALID_CITY` | 400 | Use a slug from `/api/public/cities` |
51+
52+
### Events
53+
54+
| Code | HTTP | Remedy |
55+
|---|---|---|
56+
| `EVENT_NOT_FOUND` | 404 | Event ID does not exist or is unpublished |
57+
| `EVENT_PAST` | 400 | Event already happened |
58+
| `EVENT_FULL` | 400 | Join the waitlist |
59+
| `EVENT_NOT_PUBLISHED` | 400 | Publish before selling tickets |
60+
61+
### Tickets
62+
63+
| Code | HTTP | Remedy |
64+
|---|---|---|
65+
| `ALREADY_CHECKED_IN` | 409 | Idempotent — already done |
66+
| `TICKET_REFUNDED` | 400 | Ticket is no longer valid |
67+
| `TICKET_CANCELLED` | 400 | Buyer cancelled before pay |
68+
| `REFUND_WINDOW_CLOSED` | 400 | Less than 24h before start; contact organizer |
69+
| `STRIPE_DECLINED` | 400 | Card declined — try another |
70+
71+
### Stripe / Connect
72+
73+
| Code | HTTP | Remedy |
74+
|---|---|---|
75+
| `STRIPE_NOT_ONBOARDED` | 400 | Organizer must complete Stripe onboarding |
76+
| `STRIPE_CAPABILITIES_PENDING` | 400 | Stripe still verifying organizer |
77+
| `WEBHOOK_SIGNATURE_INVALID` | 400 | Set `STRIPE_WEBHOOK_SECRET` correctly |
78+
79+
### Rate / availability
80+
81+
| Code | HTTP | Remedy |
82+
|---|---|---|
83+
| `RATE_LIMITED` | 429 | Honor `Retry-After` |
84+
| `DEPENDENCY_UNAVAILABLE` | 503 | Retry with backoff |
85+
| `INTERNAL_ERROR` | 500 | [Report it](https://github.com/ForliLabs/piazza-viva/issues) |
86+
87+
## Reporting
88+
89+
When opening an issue, include:
90+
91+
- `X-Request-Id` from the response headers (we use it to find your trace).
92+
- The full JSON body of the error response.
93+
- The exact request (method, path, headers, body) with secrets redacted.

website/docs/api/events.md

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
---
2+
id: events
3+
title: Events API
4+
description: List, search, and retrieve events.
5+
---
6+
7+
# Events API
8+
9+
The events endpoints are the heart of the public API.
10+
11+
## `GET /api/public/events`
12+
13+
List published events with filtering, sorting, and pagination.
14+
15+
### Query parameters
16+
17+
| Param | Type | Default | Description |
18+
|---|---|---|---|
19+
| `city` | string || Filter by city name |
20+
| `category` | string || Filter by category slug |
21+
| `organizer` | string (id) || Pin to one organizer |
22+
| `venue` | string (id) || Pin to one venue |
23+
| `from` | ISO date | now | Start window (inclusive) |
24+
| `to` | ISO date | +90 days | End window (inclusive) |
25+
| `q` | string || Full-text search across title + description |
26+
| `priceMax` | int (cents) || Upper price bound |
27+
| `sort` | `date` \| `popular` | `date` | Ordering |
28+
| `limit` | int 1–100 | `20` | Page size |
29+
| `offset` | int | `0` | Pagination offset |
30+
31+
### Request
32+
33+
```bash
34+
curl -H "X-API-Key: $PV_KEY" \
35+
"https://piazzaviva.it/api/public/events?city=Forlì&category=musica&limit=2"
36+
```
37+
38+
### Response
39+
40+
```json
41+
{
42+
"data": [
43+
{
44+
"id": "clx0abc123",
45+
"title": "Jazz in Piazza",
46+
"description": "Una serata di standard jazz...",
47+
"summary": "Concerto jazz gratuito in Piazza Saffi.",
48+
"category": "musica",
49+
"city": "Forlì",
50+
"venue": {
51+
"id": "ven_456",
52+
"name": "Piazza Saffi",
53+
"address": "Piazza Saffi, Forlì",
54+
"latitude": 44.2225,
55+
"longitude": 12.0408
56+
},
57+
"organizer": {
58+
"id": "org_789",
59+
"name": "Comune di Forlì",
60+
"verificationTier": "municipal_partner"
61+
},
62+
"startDate": "2025-07-15T19:00:00.000Z",
63+
"endDate": "2025-07-15T23:00:00.000Z",
64+
"priceCents": 0,
65+
"currency": "EUR",
66+
"capacity": 500,
67+
"ticketsAvailable": 412,
68+
"url": "https://piazzaviva.it/events/clx0abc123",
69+
"imageUrl": "https://cdn.piazzaviva.it/events/clx0abc123/cover.jpg"
70+
}
71+
],
72+
"meta": {"limit": 2, "offset": 0, "total": 47}
73+
}
74+
```
75+
76+
## `GET /api/public/events/[id]`
77+
78+
Fetch a single event with full detail (description, tags, photos, related events).
79+
80+
```bash
81+
curl -H "X-API-Key: $PV_KEY" \
82+
"https://piazzaviva.it/api/public/events/clx0abc123"
83+
```
84+
85+
Returns the same shape as the list item, with these additional fields:
86+
87+
| Field | Type | Description |
88+
|---|---|---|
89+
| `tags` | string[] | AI-generated tags |
90+
| `descriptionHtml` | string | Sanitized HTML rendering |
91+
| `photos` | object[] | Post-event photos (if any) |
92+
| `relatedEvents` | object[] | Up to 3 similar upcoming events |
93+
94+
404 if the event doesn't exist or isn't published.
95+
96+
## `GET /api/public/events/[id]/ical`
97+
98+
Returns an `.ics` file for calendar import. No API key required — the URL itself is unguessable per-event.
99+
100+
```bash
101+
curl "https://piazzaviva.it/api/public/events/clx0abc123/ical"
102+
```
103+
104+
## Categories
105+
106+
The full slug list:
107+
108+
```
109+
musica · cultura · sport · food · università · civico · famiglia · sostenibilità · libri
110+
```
111+
112+
Use `GET /api/public/categories` for canonical metadata (display name, icon, accent color).

0 commit comments

Comments
 (0)