-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdocs.html
More file actions
477 lines (448 loc) · 28.2 KB
/
Copy pathdocs.html
File metadata and controls
477 lines (448 loc) · 28.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#050505">
<meta name="description" content="Technical documentation for the Aesthetics API in an Acid Design interface.">
<title>Aesthetics API — Technical Documentation</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Archivo+Black&family=IBM+Plex+Mono:wght@400;500;600;700&family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="styles.css">
<link rel="stylesheet" href="docs.css">
</head>
<body class="docs-body">
<div class="noise" aria-hidden="true"></div>
<div class="page-frame" aria-hidden="true"></div>
<nav class="site-nav docs-nav">
<a class="brand" href="index.html" aria-label="Aesthetics API home">
<span class="wire-globe mini" aria-hidden="true"></span>
<span>AESTHETICS API</span>
</a>
<button class="nav-toggle" aria-expanded="false" aria-controls="nav-links">MENU</button>
<div class="nav-links" id="nav-links">
<a href="index.html">Overview</a>
<a href="#quickstart">Quickstart</a>
<a href="#reference">Reference</a>
<a href="#schemas">Schemas</a>
<a href="#deployment">Deployment</a>
<a href="#troubleshooting">Troubleshooting</a>
</div>
<a class="nav-cta" href="https://github.com/schwwaaa/aesthetics-api">GITHUB <span>→</span></a>
<!-- <span class="smiley tiny" aria-hidden="true">☺</span> -->
</nav>
<header class="docs-hero acid-section" id="top">
<div class="docs-hero-rail" aria-hidden="true">
<span>TECH REF / 01</span>
<span class="docs-barcode"></span>
<span>REST · JSON · FASTAPI</span>
</div>
<div class="docs-hero-copy">
<p class="kicker">SYSTEM DOCUMENTATION · IMPLEMENTATION REFERENCE</p>
<div class="docs-title-lockup" aria-label="Aesthetics Docs">
<img src="assets/chrome-aesthetics.png" alt="AESTHETICS" class="chrome-title docs-chrome-title">
<span class="docs-word">DOCS</span>
</div>
<p class="docs-hero-lede">A technical reference for installation, configuration, data contracts, endpoint behavior, image delivery, deployment, and failure handling.</p>
<div class="docs-meta-row">
<span><b>API</b> v1</span>
<span><b>FORMAT</b> JSON</span>
<span><b>AUTH</b> NONE</span>
<span><b>RUNTIME</b> PYTHON 3.10+</span>
</div>
<div class="hero-actions">
<a class="acid-button primary" href="#quickstart">START BUILDING <span>→</span></a>
<a class="acid-button ghost" href="#openapi">OPENAPI / DOCS <span class="spark">✦</span></a>
</div>
</div>
<div class="docs-hero-art" aria-hidden="true">
<div class="docs-grid-orb"></div>
<img src="assets/chrome-smiley.png" alt="" class="docs-smiley">
<div class="docs-orbit-text">AESTHETICS API · TECHNICAL SYSTEM · NO AUTH ·</div>
<span class="docs-hero-star">✦</span>
<div class="docs-sticker">
<b>219</b>
<span>RECORDS</span>
<small>STRUCTURED / SEARCHABLE / PORTABLE</small>
</div>
</div>
</header>
<div class="docs-shell">
<aside class="docs-sidebar" aria-label="Documentation navigation">
<button class="docs-sidebar-toggle" type="button" aria-expanded="false">DOCUMENTATION INDEX <span>+</span></button>
<div class="docs-sidebar-inner">
<div class="sidebar-system">
<span class="crosshair inline"></span>
<div><b>SYSTEM INDEX</b><small>v1.0 / ONLINE</small></div>
</div>
<nav class="docs-section-nav">
<a href="#introduction">01 / Introduction</a>
<a href="#architecture">02 / Architecture</a>
<a href="#quickstart">03 / Quickstart</a>
<a href="#configuration">04 / Configuration</a>
<a href="#reference">05 / Endpoint reference</a>
<a href="#schemas">06 / Data schemas</a>
<a href="#errors">07 / Errors</a>
<a href="#images">08 / Image delivery</a>
<a href="#cors">09 / CORS & clients</a>
<a href="#openapi">10 / OpenAPI</a>
<a href="#deployment">11 / Deployment</a>
<a href="#performance">12 / Performance</a>
<a href="#troubleshooting">13 / Troubleshooting</a>
</nav>
<div class="sidebar-poster">
<span class="wire-globe"></span>
<strong>NO KEY<br>NO GATE</strong>
<small>READ-ONLY PUBLIC DATA</small>
<div class="mini-barcode"></div>
</div>
</div>
</aside>
<main class="docs-content">
<section class="docs-section" id="introduction" data-section="Introduction">
<div class="docs-section-heading">
<span class="section-index">01</span>
<div><p>FOUNDATION</p><h1>Introduction</h1></div>
</div>
<p class="docs-lead">The Aesthetics API exposes a normalized collection of aesthetic names, slugs, source links, image filenames, and resolved image URLs through a compact read-only HTTP interface.</p>
<div class="callout callout-acid">
<span class="callout-icon">✦</span>
<div><strong>Design goal</strong><p>Keep the dataset portable and the API predictable. Records remain image-host agnostic while the server resolves the final image URL at response time.</p></div>
</div>
<div class="feature-matrix">
<article><span>01</span><h3>Read only</h3><p>No mutation endpoints, account state, sessions, or authorization flow.</p></article>
<article><span>02</span><h3>Flat records</h3><p>Small JSON objects that map directly into UI, scripts, and data pipelines.</p></article>
<article><span>03</span><h3>Portable media</h3><p>Switch between local files, a custom CDN, or source-hosted images without rewriting records.</p></article>
</div>
</section>
<section class="docs-section" id="architecture" data-section="Architecture">
<div class="docs-section-heading">
<span class="section-index">02</span>
<div><p>SYSTEM MODEL</p><h2>Architecture</h2></div>
</div>
<div class="architecture-map" aria-label="Request lifecycle diagram">
<div class="architecture-node"><small>CLIENT</small><b>HTTP REQUEST</b><code>GET /aesthetics/{slug}</code></div>
<span class="architecture-arrow">→</span>
<div class="architecture-node acid-node"><small>FASTAPI</small><b>ROUTE LAYER</b><code>validation + lookup</code></div>
<span class="architecture-arrow">→</span>
<div class="architecture-node"><small>DATA</small><b>AESTHETICS.JSON</b><code>normalized records</code></div>
<span class="architecture-arrow">→</span>
<div class="architecture-node magenta-node"><small>OUTPUT</small><b>JSON RESPONSE</b><code>resolved image_url</code></div>
</div>
<div class="two-column-copy">
<div><h3>Data loading</h3><p>The application loads the JSON dataset at startup and constructs a slug-addressable in-memory collection. This makes list, direct lookup, search, and random selection inexpensive at runtime.</p></div>
<div><h3>Image resolution</h3><p>The stored <code>image</code> value remains a filename. The response layer generates <code>image_url</code> from the active image delivery mode and base configuration.</p></div>
</div>
</section>
<section class="docs-section" id="quickstart" data-section="Quickstart">
<div class="docs-section-heading">
<span class="section-index">03</span>
<div><p>LOCAL RUNTIME</p><h2>Quickstart</h2></div>
</div>
<ol class="terminal-steps">
<li>
<span class="terminal-step-number">01</span>
<div class="terminal-step-body">
<h3>Create an isolated Python environment</h3>
<div class="technical-code"><button class="docs-copy" type="button">COPY</button><pre><code>python3 -m venv .venv
source .venv/bin/activate
# Windows PowerShell
.venv\Scripts\Activate.ps1</code></pre></div>
</div>
</li>
<li>
<span class="terminal-step-number">02</span>
<div class="terminal-step-body">
<h3>Install the server and scraper dependencies</h3>
<div class="technical-code"><button class="docs-copy" type="button">COPY</button><pre><code>python -m pip install --upgrade pip
pip install "fastapi[standard]" uvicorn requests beautifulsoup4</code></pre></div>
</div>
</li>
<li>
<span class="terminal-step-number">03</span>
<div class="terminal-step-body">
<h3>Verify the expected project files</h3>
<div class="technical-code"><button class="docs-copy" type="button">COPY</button><pre><code>project/
├── api.py
├── aesthetics.json
└── images/
├── acid-design.png
└── ...</code></pre></div>
</div>
</li>
<li>
<span class="terminal-step-number">04</span>
<div class="terminal-step-body">
<h3>Start the development server</h3>
<div class="technical-code"><button class="docs-copy" type="button">COPY</button><pre><code>uvicorn api:app --reload --host 127.0.0.1 --port 8000</code></pre></div>
</div>
</li>
</ol>
<div class="callout callout-cyan">
<span class="callout-icon">◎</span>
<div><strong>Health verification</strong><p>Open <code>http://127.0.0.1:8000/healthz</code>. A healthy process returns status, record count, and active image mode.</p></div>
</div>
</section>
<section class="docs-section" id="configuration" data-section="Configuration">
<div class="docs-section-heading">
<span class="section-index">04</span>
<div><p>RUNTIME CONTROL</p><h2>Configuration</h2></div>
</div>
<p>Keep deployment-specific values outside the dataset. The following table defines the configuration surface expected by this documentation baseline.</p>
<div class="table-wrap">
<table class="acid-table">
<thead><tr><th>Variable</th><th>Purpose</th><th>Example</th><th>Required</th></tr></thead>
<tbody>
<tr><td><code>AESTHETICS_DATA</code></td><td>Path to the JSON dataset.</td><td><code>./aesthetics.json</code></td><td>No</td></tr>
<tr><td><code>IMAGE_MODE</code></td><td>Image delivery strategy.</td><td><code>local</code>, <code>cdn</code>, <code>source</code></td><td>No</td></tr>
<tr><td><code>IMAGE_DIR</code></td><td>Filesystem directory used by local image mode.</td><td><code>./images</code></td><td>Local mode</td></tr>
<tr><td><code>IMAGE_BASE_URL</code></td><td>Public base URL used by custom CDN mode.</td><td><code>https://cdn.example.com/aesthetics</code></td><td>CDN mode</td></tr>
<tr><td><code>PUBLIC_API_BASE</code></td><td>Optional public base used when constructing absolute local URLs.</td><td><code>https://api.example.com</code></td><td>No</td></tr>
</tbody>
</table>
</div>
<div class="technical-code"><button class="docs-copy" type="button">COPY</button><pre><code># Example local configuration
export AESTHETICS_DATA="./aesthetics.json"
export IMAGE_MODE="local"
export IMAGE_DIR="./images"
uvicorn api:app --host 0.0.0.0 --port 8000</code></pre></div>
</section>
<section class="docs-section endpoint-reference" id="reference" data-section="Endpoint reference">
<div class="docs-section-heading">
<span class="section-index">05</span>
<div><p>HTTP CONTRACT</p><h2>Endpoint reference</h2></div>
</div>
<article class="reference-route" id="get-aesthetics">
<header><span class="method-badge">GET</span><code>/aesthetics</code><span class="response-badge">200 JSON</span></header>
<p>Returns the complete collection. Optional pagination parameters can be applied by clients that do not want the entire payload.</p>
<div class="route-spec-grid">
<div><h3>Query parameters</h3><dl class="parameter-list"><div><dt><code>limit</code></dt><dd>Optional integer cap.</dd></div><div><dt><code>offset</code></dt><dd>Optional zero-based starting position.</dd></div></dl></div>
<div><h3>Response shape</h3><p>Array of <a href="#aesthetic-schema"><code>Aesthetic</code></a> objects.</p></div>
</div>
<div class="language-tabs" data-code-tabs>
<div class="language-tab-list"><button class="is-active" data-lang="curl">cURL</button><button data-lang="javascript">JavaScript</button><button data-lang="python">Python</button></div>
<div class="language-code is-active" data-code="curl"><button class="docs-copy" type="button">COPY</button><pre><code>curl -s "http://127.0.0.1:8000/aesthetics?limit=20&offset=0"</code></pre></div>
<div class="language-code" data-code="javascript"><button class="docs-copy" type="button">COPY</button><pre><code>const response = await fetch(
"http://127.0.0.1:8000/aesthetics?limit=20&offset=0"
);
const aesthetics = await response.json();</code></pre></div>
<div class="language-code" data-code="python"><button class="docs-copy" type="button">COPY</button><pre><code>import requests
aesthetics = requests.get(
"http://127.0.0.1:8000/aesthetics",
params={"limit": 20, "offset": 0},
timeout=10,
).json()</code></pre></div>
</div>
</article>
<article class="reference-route" id="get-aesthetic">
<header><span class="method-badge">GET</span><code>/aesthetics/{slug}</code><span class="response-badge">200 / 404</span></header>
<p>Returns one exact record selected by canonical slug.</p>
<div class="route-spec-grid">
<div><h3>Path parameter</h3><dl class="parameter-list"><div><dt><code>slug</code></dt><dd>Required lowercase URL-safe record identifier.</dd></div></dl></div>
<div><h3>Failure behavior</h3><p>An unknown slug returns <code>404</code> with a JSON error body.</p></div>
</div>
<div class="technical-code"><button class="docs-copy" type="button">COPY</button><pre><code>curl -s http://127.0.0.1:8000/aesthetics/acid-design</code></pre></div>
</article>
<article class="reference-route" id="get-image">
<header><span class="method-badge">GET</span><code>/aesthetics/{slug}/image</code><span class="response-badge">IMAGE / REDIRECT</span></header>
<p>Resolves the primary moodboard media for a record. The exact transport behavior depends on the configured image mode.</p>
<div class="route-spec-grid">
<div><h3>Local mode</h3><p>Returns the file response from the configured image directory.</p></div>
<div><h3>Remote modes</h3><p>Redirects to the generated CDN URL or the record's source image URL.</p></div>
</div>
</article>
<article class="reference-route" id="get-random">
<header><span class="method-badge">GET</span><code>/random</code><span class="response-badge">200 JSON</span></header>
<p>Returns one randomly selected aesthetic record.</p>
<div class="technical-code"><button class="docs-copy" type="button">COPY</button><pre><code>curl -s http://127.0.0.1:8000/random</code></pre></div>
</article>
<article class="reference-route" id="search">
<header><span class="method-badge">GET</span><code>/search?q={query}</code><span class="response-badge">200 / 422</span></header>
<p>Performs a case-insensitive substring search against aesthetic names.</p>
<div class="route-spec-grid">
<div><h3>Query parameter</h3><dl class="parameter-list"><div><dt><code>q</code></dt><dd>Required search string.</dd></div></dl></div>
<div><h3>Response shape</h3><p>Array of matching <code>Aesthetic</code> objects. No match returns an empty array.</p></div>
</div>
<div class="technical-code"><button class="docs-copy" type="button">COPY</button><pre><code>curl -s "http://127.0.0.1:8000/search?q=core"</code></pre></div>
</article>
<article class="reference-route" id="healthz">
<header><span class="method-badge">GET</span><code>/healthz</code><span class="response-badge">200 JSON</span></header>
<p>Provides a lightweight liveness response and confirms that the dataset was loaded.</p>
<div class="technical-code"><button class="docs-copy" type="button">COPY</button><pre><code>{
"status": "ok",
"count": 219,
"image_mode": "local"
}</code></pre></div>
</article>
</section>
<section class="docs-section" id="schemas" data-section="Data schemas">
<div class="docs-section-heading">
<span class="section-index">06</span>
<div><p>DATA CONTRACT</p><h2>Data schemas</h2></div>
</div>
<article class="schema-card" id="aesthetic-schema">
<header><h3>Aesthetic</h3><span>OBJECT</span></header>
<div class="table-wrap">
<table class="acid-table schema-table">
<thead><tr><th>Field</th><th>Type</th><th>Nullable</th><th>Description</th></tr></thead>
<tbody>
<tr><td><code>slug</code></td><td>string</td><td>No</td><td>Canonical URL-safe identifier.</td></tr>
<tr><td><code>name</code></td><td>string</td><td>No</td><td>Human-readable aesthetic name.</td></tr>
<tr><td><code>aesthetic_url</code></td><td>URL string</td><td>No</td><td>Source article on the Aesthetics Wiki.</td></tr>
<tr><td><code>image</code></td><td>string</td><td>No</td><td>Portable image filename stored in the dataset.</td></tr>
<tr><td><code>source_image_url</code></td><td>URL string</td><td>Possible</td><td>Original source-hosted media URL.</td></tr>
<tr><td><code>image_url</code></td><td>URL string</td><td>No</td><td>Resolved public image location for the active server mode.</td></tr>
</tbody>
</table>
</div>
<div class="technical-code schema-example"><button class="docs-copy" type="button">COPY</button><pre><code>{
"slug": "acid-design",
"name": "Acid Design",
"aesthetic_url": "https://aesthetics.fandom.com/wiki/Acid_Design",
"image": "acid-design.png",
"source_image_url": "https://static.wikia.nocookie.net/.../revision/latest",
"image_url": "http://127.0.0.1:8000/images/acid-design.png"
}</code></pre></div>
</article>
</section>
<section class="docs-section" id="errors" data-section="Errors">
<div class="docs-section-heading">
<span class="section-index">07</span>
<div><p>FAILURE CONTRACT</p><h2>Errors</h2></div>
</div>
<div class="error-grid">
<article><span>404</span><div><h3>Unknown record</h3><p>The requested slug is not present in the loaded dataset.</p></div></article>
<article><span>404</span><div><h3>Missing local image</h3><p>The record exists, but the configured local image file cannot be read.</p></div></article>
<article><span>422</span><div><h3>Invalid request</h3><p>A required path or query value failed request validation.</p></div></article>
<article><span>500</span><div><h3>Server failure</h3><p>The process could not load or serialize required application data.</p></div></article>
</div>
<div class="technical-code"><button class="docs-copy" type="button">COPY</button><pre><code>{
"detail": "Aesthetic not found"
}</code></pre></div>
</section>
<section class="docs-section" id="images" data-section="Image delivery">
<div class="docs-section-heading">
<span class="section-index">08</span>
<div><p>MEDIA ROUTING</p><h2>Image delivery</h2></div>
</div>
<div class="mode-switcher" role="tablist" aria-label="Image modes">
<button class="is-active" data-mode="local">LOCAL</button>
<button data-mode="cdn">CUSTOM CDN</button>
<button data-mode="source">SOURCE HOST</button>
</div>
<div class="mode-panels">
<article class="mode-panel is-active" data-mode-panel="local"><span>01</span><div><h3>Local filesystem</h3><p>The API mounts or streams files from <code>IMAGE_DIR</code>. JSON responses resolve to the API's own image route or static mount.</p><code>http://127.0.0.1:8000/images/acid-design.png</code></div></article>
<article class="mode-panel" data-mode-panel="cdn"><span>02</span><div><h3>Custom CDN</h3><p>The server joins <code>IMAGE_BASE_URL</code> with each record's portable filename.</p><code>https://cdn.example.com/aesthetics/acid-design.png</code></div></article>
<article class="mode-panel" data-mode-panel="source"><span>03</span><div><h3>Source host</h3><p>The server exposes or redirects to <code>source_image_url</code>, avoiding local image storage.</p><code>https://static.wikia.nocookie.net/...</code></div></article>
</div>
</section>
<section class="docs-section" id="cors" data-section="CORS and clients">
<div class="docs-section-heading">
<span class="section-index">09</span>
<div><p>BROWSER ACCESS</p><h2>CORS & clients</h2></div>
</div>
<p>The landing-page explorer calls the API directly from the browser. Development deployments therefore need an origin policy that allows the page's host and HTTP method.</p>
<div class="callout callout-magenta"><span class="callout-icon">!</span><div><strong>Production policy</strong><p>Use explicit allowed origins for a public deployment rather than unrestricted wildcard configuration when credentials or private endpoints are ever introduced.</p></div></div>
<div class="technical-code"><button class="docs-copy" type="button">COPY</button><pre><code>from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=["https://docs.example.com"],
allow_methods=["GET"],
allow_headers=["Accept", "Content-Type"],
)</code></pre></div>
</section>
<section class="docs-section" id="openapi" data-section="OpenAPI">
<div class="docs-section-heading">
<span class="section-index">10</span>
<div><p>MACHINE DESCRIPTION</p><h2>OpenAPI</h2></div>
</div>
<div class="openapi-grid">
<a href="http://127.0.0.1:8000/docs" target="_blank" rel="noopener"><span>SWAGGER UI</span><strong>/docs</strong><small>Interactive route documentation</small></a>
<a href="http://127.0.0.1:8000/redoc" target="_blank" rel="noopener"><span>RE-DOC</span><strong>/redoc</strong><small>Readable schema reference</small></a>
<a href="http://127.0.0.1:8000/openapi.json" target="_blank" rel="noopener"><span>SCHEMA</span><strong>/openapi.json</strong><small>Machine-readable contract</small></a>
</div>
</section>
<section class="docs-section" id="deployment" data-section="Deployment">
<div class="docs-section-heading">
<span class="section-index">11</span>
<div><p>PRODUCTION RUNTIME</p><h2>Deployment</h2></div>
</div>
<div class="deployment-grid">
<article><span>01</span><h3>Bind externally</h3><p>Use <code>0.0.0.0</code> inside a container or hosted runtime.</p></article>
<article><span>02</span><h3>Proxy TLS</h3><p>Terminate HTTPS at a reverse proxy or platform edge.</p></article>
<article><span>03</span><h3>Persist media</h3><p>Use a durable volume or CDN if local files must survive redeploys.</p></article>
<article><span>04</span><h3>Probe health</h3><p>Configure platform liveness checks against <code>/healthz</code>.</p></article>
</div>
<div class="language-tabs" data-code-tabs>
<div class="language-tab-list"><button class="is-active" data-lang="dockerfile">Dockerfile</button><button data-lang="compose">Compose</button><button data-lang="shell">Shell</button></div>
<div class="language-code is-active" data-code="dockerfile"><button class="docs-copy" type="button">COPY</button><pre><code>FROM python:3.12-slim
WORKDIR /app
COPY . .
RUN pip install --no-cache-dir "fastapi[standard]" uvicorn
EXPOSE 8000
CMD ["uvicorn", "api:app", "--host", "0.0.0.0", "--port", "8000"]</code></pre></div>
<div class="language-code" data-code="compose"><button class="docs-copy" type="button">COPY</button><pre><code>services:
aesthetics-api:
build: .
ports:
- "8000:8000"
environment:
IMAGE_MODE: local
volumes:
- ./images:/app/images:ro</code></pre></div>
<div class="language-code" data-code="shell"><button class="docs-copy" type="button">COPY</button><pre><code>uvicorn api:app \
--host 0.0.0.0 \
--port "${PORT:-8000}" \
--workers 2</code></pre></div>
</div>
</section>
<section class="docs-section" id="performance" data-section="Performance">
<div class="docs-section-heading">
<span class="section-index">12</span>
<div><p>OPERATIONS</p><h2>Performance</h2></div>
</div>
<div class="performance-strip">
<article><strong>O(1)</strong><span>Direct slug lookup</span></article>
<article><strong>O(n)</strong><span>Substring search</span></article>
<article><strong>~219</strong><span>Current record scale</span></article>
<article><strong>GET</strong><span>Read-only traffic</span></article>
</div>
<p>For this dataset size, in-memory access is appropriate. Add response compression and cache headers at the reverse proxy when the full collection or remote images receive repeated public traffic.</p>
</section>
<section class="docs-section" id="troubleshooting" data-section="Troubleshooting">
<div class="docs-section-heading">
<span class="section-index">13</span>
<div><p>DIAGNOSTICS</p><h2>Troubleshooting</h2></div>
</div>
<div class="accordion-list">
<details open><summary>The explorer reports “offline” or shows sample data.<span>+</span></summary><div><p>Confirm that Uvicorn is running, that the Base URL matches its host and port, and that browser CORS policy allows the documentation page's origin.</p><code>curl -i http://127.0.0.1:8000/healthz</code></div></details>
<details><summary>The API starts but reports zero records.<span>+</span></summary><div><p>Verify the dataset path, JSON validity, file permissions, and that the application is started from the expected working directory.</p><code>python -m json.tool aesthetics.json > /dev/null</code></div></details>
<details><summary>JSON works but image endpoints return 404.<span>+</span></summary><div><p>Check <code>IMAGE_MODE</code>, confirm that each record's <code>image</code> filename exists under <code>IMAGE_DIR</code>, and inspect case sensitivity on Linux hosts.</p></div></details>
<details><summary>Remote images fail in a browser client.<span>+</span></summary><div><p>The upstream host may reject hotlinking or apply its own CORS policy. Use a server-side redirect, local mirror, or controlled CDN where licensing allows it.</p></div></details>
</div>
</section>
<footer class="docs-content-footer">
<span class="wire-globe mini"></span>
<div><strong>AESTHETICS API / TECHNICAL DOCUMENTATION</strong><p>Built as a dense companion to the visual landing page.</p></div>
<a href="#top">BACK TO TOP ↑</a>
</footer>
</main>
<aside class="docs-toc" aria-label="On this page">
<div class="toc-title"><span>⌖</span> ON THIS PAGE</div>
<a href="#introduction">Introduction</a>
<a href="#architecture">Architecture</a>
<a href="#quickstart">Quickstart</a>
<a href="#configuration">Configuration</a>
<a href="#reference">Endpoint reference</a>
<a href="#schemas">Data schemas</a>
<a href="#errors">Errors</a>
<a href="#images">Image delivery</a>
<a href="#deployment">Deployment</a>
<a href="#troubleshooting">Troubleshooting</a>
<div class="toc-status"><span></span><b>DOCS ONLINE</b><small>LAST BUILD / STATIC</small></div>
</aside>
</div>
<script src="docs.js"></script>
</body>
</html>