Skip to content

Commit f201e63

Browse files
authored
Merge pull request #2 from yifen9/refactor-api
Refactor api
2 parents 62abd99 + 1f70e34 commit f201e63

File tree

135 files changed

+5011
-4347
lines changed

Some content is hidden

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

135 files changed

+5011
-4347
lines changed

biome.json

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,27 @@
2323
"organizeImports": "on"
2424
}
2525
}
26-
}
26+
},
27+
"overrides": [
28+
{
29+
"includes": ["frontend/src/app.css"],
30+
"linter": {
31+
"rules": {
32+
"suspicious": { "noUnknownAtRules": "off" },
33+
"correctness": { "noUnknownProperty": "off" }
34+
}
35+
}
36+
},
37+
{
38+
"includes": ["**/*.svelte"],
39+
"linter": {
40+
"rules": {
41+
"correctness": {
42+
"noUnusedVariables": "off",
43+
"noUnusedImports": "off"
44+
}
45+
}
46+
}
47+
}
48+
]
2749
}

deploy/cloudflare/dev/wrangler.jsonc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
{
1212
"binding": "DB",
1313
"database_name": "unitn-oj-dev",
14-
"database_id": "7eed840d-0135-441a-a24d-d4ec7133a1f1",
14+
"database_id": "d04624fd-bfd2-4817-a84e-47cc5717e839",
1515
"migrations_dir": "../../../frontend/d1/migrations"
1616
}
1717
],
@@ -35,7 +35,7 @@
3535
{
3636
"binding": "DB",
3737
"database_name": "unitn-oj-dev",
38-
"database_id": "7eed840d-0135-441a-a24d-d4ec7133a1f1",
38+
"database_id": "d04624fd-bfd2-4817-a84e-47cc5717e839",
3939
"migrations_dir": "../../../frontend/d1/migrations"
4040
}
4141
],

docs/frontend/api-overview.md

Lines changed: 114 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# API Overview
22

3-
> Version: v1 (base path: `/api/v1/`)
3+
> Version: `v1` (base path: `/api/v1/`), except `/api/health` which is unversioned for operational readiness. Google’s AIP recommends URI-segment versioning and keeping one GA “latest” at a time. ([aip.dev][1])
44
5-
All responses are JSON.
5+
All successful responses use `application/json` with a minimal envelope; all errors use `application/problem+json` per **RFC 9457** (which **obsoletes RFC 7807**). Servers **MUST** set the appropriate `Content-Type`. ([datatracker.ietf.org][2])
66

77
Success
88

@@ -13,167 +13,213 @@ Success
1313
Error
1414

1515
```json
16-
{ "ok": false, "error": { "code": "INVALID_ARGUMENT", "message": "..." } }
16+
{
17+
"type": "about:blank",
18+
"title": "Invalid argument",
19+
"status": 400,
20+
"detail": "",
21+
"instance": "/api/v1/…"
22+
}
1723
```
1824

19-
## Error Codes
25+
Error bodies follow RFC 9457; custom members are allowed for machine-readable details. ([rfc-editor.org][3])
26+
27+
## Versioning
28+
29+
Use `v1` as the base path and evolve compatibly; ship breaking changes as `v2`. Keep a single “latest” GA version; deprecate previews quickly. ([aip.dev][1])
30+
31+
## Media types & content negotiation
32+
33+
Clients SHOULD send `Accept: application/json`. The server MAY honor `+json` types via the structured syntax suffix (e.g., `application/problem+json`). If `Accept` excludes JSON, respond `406 Not Acceptable`. ([datatracker.ietf.org][4])
2034

21-
`INVALID_ARGUMENT` · `FAILED_PRECONDITION` · `UNAUTHENTICATED` · `PERMISSION_DENIED` · `NOT_FOUND` · `RESOURCE_EXHAUSTED` · `INTERNAL`
35+
## HTTP status
2236

23-
## HTTP Status
37+
Use standard semantics: `200` for successful reads, `201` for creations, `401` for missing/invalid session, `403` for forbidden, `404` for not found, `415` for non-JSON request bodies, and `5xx` for server-side failures. ([rfc-editor.org][5])
2438

25-
* 200 OK – Successful read or non-creating action
26-
* 201 Created – Resource created (e.g. submission)
27-
* 401 Unauthorized – Missing/invalid `sid`
28-
* 403 Forbidden – Authenticated but not allowed (e.g. not owner)
29-
* 404 Not Found – Resource does not exist
30-
* 415 Unsupported Media Type – JSON required
31-
* 5xx – Server-side errors (prod only leaks generic message)
39+
## Caching
40+
41+
Unless documented otherwise, endpoints return `Cache-Control: no-store` to avoid reuse of potentially stale representations (notably health, auth, and session-scoped reads). ([MDN Web Docs][6])
42+
43+
## Tracing
44+
45+
Requests may include `traceparent`. The server will echo the incoming value or generate a new one in **W3C Trace Context** format to support end-to-end correlation. ([W3C][7])
3246

3347
## Authentication
3448

35-
* Session cookie: `sid`
49+
A session cookie named `sid` is set on successful login. Attributes: `HttpOnly; Path=/; SameSite=Lax; Max-Age=<env>` and `Secure` in production. Sign-in is restricted to `@studenti.unitn.it`. In development, the magic link is returned inline for convenience; in production, only a generic success is returned and the link is delivered by email. ([Google Cloud][8])
50+
51+
---
52+
53+
# Endpoints
3654

37-
* Attributes: `HttpOnly; Path=/; SameSite=Lax; Max-Age=<env>;` plus `Secure` in production
38-
* Allowed email domain: `@studenti.unitn.it`
39-
* In dev, magic link is returned in the response; in prod it is sent via email
55+
## Health (unversioned)
56+
57+
### `GET /api/health`
58+
59+
Returns `200` with `{ "ok": true, "data": { "db": "ok" | "skipped", "time": <epoch> } }` when dependencies are reachable; otherwise `503` with a problem document. Always `Cache-Control: no-store`. If `Accept` excludes JSON (including all `+json` types), return `406` with a problem document. ([rfc-editor.org][5], [datatracker.ietf.org][4])
60+
61+
### `HEAD /api/health`
62+
63+
Returns `200` (healthy) or `503` (unhealthy) with no body; always `Cache-Control: no-store`. Rationale: an unversioned health path avoids breaking automation and aligns with operational probes that expect a stable URL. ([aip.dev][1])
4064

4165
---
4266

4367
## Auth
4468

45-
### POST `/auth/requestLink`
69+
All request bodies MUST be `application/json`; otherwise return `415` with a problem document. ([rfc-editor.org][5])
70+
71+
### `POST /api/v1/auth/requestLink`
4672

4773
Input
4874

4975
```json
5076
{ "email": "user@studenti.unitn.it" }
5177
```
5278

53-
Output (dev)
79+
Output (development)
5480

5581
```json
5682
{ "ok": true, "data": { "magicUrl": "https://.../auth/verify?token=..." } }
5783
```
5884

59-
Output (prod)
85+
Output (production)
6086

6187
```json
6288
{ "ok": true }
6389
```
6490

65-
Purpose: Request a magic link to sign in.
91+
Issues a one-time login token if the email domain is allowed. In development the `magicUrl` is returned; in production, the server sends mail and returns a generic success. ([Google Cloud][8])
92+
93+
### `GET /api/v1/auth/verify?token=…`
6694

67-
### GET `/auth/verify?token=...`
95+
### `POST /api/v1/auth/verify`
6896

69-
* Input: `token` in query (or POST body)
70-
* Output
97+
Verifies a one-time token, upserts the user, establishes a session, and sets the `sid` cookie.
98+
99+
Success
71100

72101
```json
73102
{ "ok": true, "data": { "userId": "u_...", "email": "user@studenti.unitn.it" } }
74103
```
75104

76-
Purpose: Verify token, upsert user, issue `sid` cookie.
105+
Invalid/expired tokens return `401` with a problem document. ([rfc-editor.org][5])
77106

78-
### POST `/auth/logout`
107+
### `POST /api/v1/auth/logout`
79108

80-
Output
81-
82-
```json
83-
{ "ok": true }
84-
```
85-
86-
Purpose: Clear `sid` cookie.
109+
Clears the `sid` cookie and returns `{ "ok": true }`. In production the cookie is set/cleared with `Secure`. ([rfc-editor.org][5])
87110

88111
---
89112

90113
## Users
91114

92-
### GET `/users/me`
115+
### `GET /api/v1/users/me`
93116

94-
Output
117+
Returns the authenticated user or `401` if the session is missing/invalid.
95118

96119
```json
97-
{ "ok": true, "data": { "userId": "u_...", "email": "..." } }
120+
{ "ok": true, "data": { "id": "u_...", "slug": "alice", "email": "" } }
98121
```
99122

100-
Purpose: Return the current authenticated user.
123+
Use `403` for authenticated-but-forbidden access to other users’ resources. ([rfc-editor.org][5])
101124

102-
---
125+
### `GET /api/v1/users/{slug}`
103126

104-
## Schools
127+
Public, cache-negotiated read (ETag/If-None-Match supported by the handler), returning basic profile fields; `404` if not found. Slugs are stable, URL-safe identifiers intended for discovery and navigation. ([aip.dev][1])
105128

106-
### GET `/schools`
129+
### `GET /api/v1/users`
107130

108-
Output
131+
Lists users with basic fields; future versions may introduce pagination tokens and filters consistent with AIP-158/160. ([aip.dev][9], [Google AIP][10])
109132

110-
```json
111-
{ "ok": true, "data": [ { "schoolId": "unitn", "name": "University of Trento" } ] }
112-
```
133+
### `GET /api/v1/users/{slug}/submissions`
134+
135+
Returns public submissions for a given user, newest first. Authorization rules may further restrict fields in non-public scenarios. ([Google Cloud][8])
136+
137+
---
138+
139+
## Schools
113140

114-
Purpose: List all schools.
141+
### `GET /api/v1/schools`
115142

116-
### GET `/schools/{id}`
143+
Returns an array of schools (id/slug/name/updated\_at). List endpoints are designed to adopt cursor pagination in a future revision (AIP-158). ([aip.dev][9])
117144

118-
Purpose: Get a school by id.
145+
### `GET /api/v1/schools/{school}`
146+
147+
Retrieves a school by **slug**; `404` if not found. Slug-based resource naming supports human-readable discovery while remaining URL-safe. ([aip.dev][1])
119148

120149
---
121150

122151
## Courses
123152

124-
### GET `/courses`
153+
### `GET /api/v1/schools/{school}/courses`
125154

126-
Purpose: List courses. Optional filter by `schoolId`.
155+
Lists courses under a school (by school **slug**). Future revisions may add pagination and filtering (AIP-158/160). ([aip.dev][9], [Google AIP][10])
127156

128-
### GET `/courses/{id}`
157+
### `GET /api/v1/schools/{school}/courses/{course}`
129158

130-
Purpose: Get a course by id.
159+
Retrieves a course by `{school slug}/{course slug}`; `404` if not found. Hierarchical URIs mirror containment and improve navigability. ([aip.dev][1])
131160

132161
---
133162

134163
## Problems
135164

136-
### GET `/courses/{courseId}/problems`
165+
### `GET /api/v1/schools/{school}/courses/{course}/problems`
166+
167+
Lists problems under a course (by slugs). This endpoint is for discovery/navigation. ([aip.dev][1])
168+
169+
### `GET /api/v1/schools/{school}/courses/{course}/problems/{problem}`
137170

138-
Purpose: List problems under a course.
171+
Retrieves a problem by three slugs. The response contains metadata (name/description), language limits, code/time/memory limits, and an optional structured `artifact` (JSON). ([Cloudflare Docs][11])
139172

140-
### GET `/courses/{courseId}/problems/{id}`
173+
### `GET /api/v1/problems/{id}`
141174

142-
Purpose: Get problem detail.
175+
Retrieves a problem by **id** (stable identifier). ID-based paths support “stable addressing” from logs, queues, or emails without requiring the hierarchical context. This dual model—slug for discovery, id for stability—follows common AIP guidance on resource names. ([aip.dev][1])
143176

144177
---
145178

146179
## Submissions
147180

148-
### POST `/problems/{id}/submissions`
181+
### `POST /api/v1/problems/{id}/submissions`
149182

150-
Input
183+
Creates a submission for a problem id; body:
151184

152185
```json
153-
{ "code": "..." }
186+
{ "code": "", "language": "cpp23" }
154187
```
155188

156-
Output (201)
189+
Response (`201 Created`)
157190

158191
```json
159192
{
160193
"ok": true,
161194
"data": {
162-
"submissionId": "s_...",
163-
"userId": "u_...",
164-
"problemId": "...",
165-
"status": "queued",
195+
"submissionId": "s_",
196+
"userId": "u_",
197+
"problemId": "",
198+
"status": "IQ",
166199
"createdAt": 1736272000
167200
}
168201
}
169202
```
170203

171-
Purpose: Create a submission (enqueued for judging).
204+
The server enqueues an asynchronous judge task and returns `201` with the resource representation; failures return a problem document. ([rfc-editor.org][5])
205+
206+
### `GET /api/v1/submissions/{id}`
207+
208+
Retrieves a submission by id (public fields). If private data exists, apply authorization (owner-only `403`). `404` if not found. ([rfc-editor.org][5])
172209

173-
### GET `/submissions/{id}`
210+
### `GET /api/v1/problems/{id}/submissions`
174211

175-
Purpose: Get submission detail (must be owner).
212+
Lists public submissions for a given problem (newest first). ([Google Cloud][8])
213+
214+
### `GET /api/v1/users/{slug}/submissions`
215+
216+
Lists public submissions for a given user (newest first). ([Google Cloud][8])
217+
218+
> Notes on lists: while the current API supports a simple `limit`, future versions are expected to adopt **AIP-158** token-based pagination and optional **AIP-160** filters. ([aip.dev][9], [Google AIP][10])
219+
220+
---
176221

177-
### GET `/users/me/submissions`
222+
# Error Model
178223

179-
Purpose: List the current user’s submissions (newest first).
224+
* All errors use **RFC 9457** (`type`, `title`, `status`, `detail`, `instance`), with optional custom members. Use `Content-Type: application/problem+json`. When `Accept` excludes JSON, respond `406` with a problem document to explain negotiation failure. ([datatracker.ietf.org][2])
225+
* JSON family is recognized via the `+json` structured syntax suffix, so `application/problem+json` participates in negotiation correctly. ([datatracker.ietf.org][4])

0 commit comments

Comments
 (0)