2
Arsitektur
github-roast adalah aplikasi SvelteKit yang di-build oleh @sveltejs/adapter-cloudflare dan disajikan sebagai Cloudflare Pages svelte.config.js:1-131. Aplikasi hanya punya dua permukaan: satu halaman yang dirender di browser dan satu endpoint server. Seluruh logika server, termasuk panggilan ke GitHub dan OpenAI, berada di dalam satu berkas, src/routes/llama/+server.js:29-312.
Lapisan dan batas kepercayaan
graph TD
subgraph browser["Browser, input belum dipercaya"]
page["+page.svelte / handleRoast"]
markdown["SvelteMarkdown"]
end
subgraph edge["Cloudflare Pages, runtime server"]
layout["+layout.svelte / slot"]
server["POST /llama"]
cache["SELECT roasts ... gh_username, language"]
end
d1[("D1 binding DB")]
gh["api.github.com/users"]
raw["raw.githubusercontent.com README.md"]
oai["OpenAI gpt-4o-mini"]
reject["400 invalid language specified"]
layout --> page
page -->|"fetch POST /llama"| server
server -->|"cek validLanguages"| reject
server -->|"cache hit"| cache
cache --- d1
server -->|"INSERT roasts"| d1
server -->|"profil dan repos"| gh
server -->|"README"| raw
server -->|"chat.completions"| oai
oai -->|"roast"| server
server -->|"json roast"| page
page --> markdown
style d1 fill:#336791,color:#fff
style cache fill:#8b5cf6,color:#fff
style server fill:#f59e0b,color:#000
style reject fill:#dc2626,color:#fff
style oai fill:#2d7d46,color:#fff
%% fig: Lapisan github-roast dan batas kepercayaan
%% desc: Halaman Svelte di browser memanggil satu endpoint /llama di Cloudflare Pages, yang membaca D1, GitHub, dan OpenAI.Lapisan browser hanya berisi satu halaman: +page.svelte memegang state username, selectedLanguage, roast, dan loading, lalu mengirim POST ke /llama dengan body { username, language } src/routes/+page.svelte:34-453. Hasil data.roast ditampilkan lewat SvelteMarkdown, sehingga teks dari model diperlakukan sebagai Markdown, bukan HTML mentah src/routes/+page.svelte:104-1114. +layout.svelte tidak menambah apa pun selain import '../app.css' dan <slot /> src/routes/+layout.svelte:1-55, jadi tidak ada lapisan pembungkus yang menyembunyikan alur data.
Halaman itu sendiri dijadikan aset statis: +page.js mengekspor prerender = true dengan alasan tidak ada data dinamis di halaman src/routes/+page.js:1-36. Batas kepercayaan karena itu jatuh tepat di /llama: POST menerima request dan platform sebagai satu-satunya pintu masuk data dari luar src/routes/llama/+server.js:29-312.
Di sisi server, input divalidasi terhadap daftar validLanguages src/routes/llama/+server.js:15-277. Daftar ini lebih pendek daripada daftar languages di halaman, yang juga memuat italian, german, finnish, dan portuguese src/routes/+page.svelte:12-288. Akibatnya keempat nilai itu ditolak oleh json(..., { status: 400 }) walau bisa dipilih di UI src/routes/llama/+server.js:33-389.
Rahasia tidak pernah menyeberang ke browser. OPENAI_API_KEY dan GITHUB_API_KEY diimpor dari $env/static/private, lalu OPENAI_API_KEY langsung dipakai membentuk instance OpenAI src/routes/llama/+server.js:1-710. GITHUB_API_KEY hanya disisipkan sebagai header Authorization di sisi server src/routes/llama/+server.js:49-5111.
Peran tiap berkas
| Lapisan | Berkas | Tanggung jawab | Batas kepercayaan |
|---|---|---|---|
| Layout | src/routes/+layout.svelte | memuat app.css, merender <slot /> | tidak menyentuh data src/routes/+layout.svelte:1-55 |
| Halaman | src/routes/+page.svelte | form username dan bahasa, handleRoast, render Markdown | menerima input pengguna apa adanya src/routes/+page.svelte:34-453 |
| Prerender | src/routes/+page.js | export const prerender = true | halaman jadi aset statis src/routes/+page.js:1-36 |
| Endpoint | src/routes/llama/+server.js | validasi bahasa, cache D1, GitHub, OpenAI | satu-satunya kode server src/routes/llama/+server.js:29-312 |
| Build | svelte.config.js | adapter() dari @sveltejs/adapter-cloudflare, vitePreprocess() | menentukan target runtime svelte.config.js:1-131 |
Ketergantungan keluar dari sisi server
Endpoint /llama memanggil empat tujuan luar. Tabel ini hanya memetakan ketergantungannya; urutan pemanggilan dibahas di Alur Permintaan Roast dan bentuk datanya di Pengumpulan Data Profil GitHub.
| Tujuan | Ekspresi | Dipakai untuk | Rujukan |
|---|---|---|---|
| GitHub user | https://api.github.com/users/${username} | profil, dengan fallback tanpa Authorization jika request gagal | src/routes/llama/+server.js:57-7112 |
| GitHub repos | https://api.github.com/users/${username}/repos?sort=updated | bahan last_15_repositories | src/routes/llama/+server.js:78-8113 |
| README mentah | raw.githubusercontent.com/${username}/${username}/main/README.md, lalu master | profile_readme | src/routes/llama/+server.js:83-10814 |
| OpenAI | client.chat.completions.create dengan model gpt-4o-mini | menghasilkan roast | src/routes/llama/+server.js:180-19115 |
Binding dan konfigurasi runtime
wrangler.toml menamai proyek githubroast, menetapkan compatibility_date = "2024-07-29", dan mengarahkan output build ke .svelte-kit/cloudflare wrangler.toml:1-416. Satu-satunya binding aktif adalah D1: binding = "DB" untuk database_name = "githubroast" wrangler.toml:28-3117. Endpoint mengaksesnya lewat platform.env.DB.prepare(...) untuk pembacaan cache wrangler.toml:28-3117 dan platform.env.DB.prepare(...).bind(...).run() untuk penulisan src/routes/llama/+server.js:194-20718.
⚠️
platform.env.DBhanya terisi di runtime Cloudflare dengan bindingDBaktif wrangler.toml:28-3117. Scriptpreviewmenjalankannpm run build && wrangler pages devpackage.json:819, dan jalur itulah yang menyediakan binding tersebut saat pengujian lokal.
⚠️ Penulisan hasil roast dibungkus
try {} catch {}kosong src/routes/llama/+server.js:194-20718. KegagalanINSERTtidak pernah menggagalkanresponse, sehingga pengguna tetap menerima roast yang tidak tersimpan di D1. Konsekuensi skema dan kolomnya dibahas di Model Data Tabel roasts.
Jalur cache punya efek arsitektur yang perlu diketahui: SELECT * FROM roasts WHERE gh_username = ? AND language = ? dijalankan sebelum panggilan eksternal apa pun, dan bila ada baris yang cocok, response dikembalikan tanpa memanggil GitHub maupun OpenAI src/routes/llama/+server.js:40-4720. Artinya biaya dan latensi dua penyedia luar itu hanya muncul pada kombinasi username dan language yang belum pernah di-roast.
Untuk detail kontrak request dan response endpoint ini, lihat Endpoint POST /llama; untuk nilai environment dan binding yang harus diisi, lihat Konfigurasi Environment dan Binding.