Skip to content

Commit a8512ea

Browse files
Replace redirect flow with inline submit via Cloudflare Worker
The previous flow took users off the page to a GitHub issue form. Now everything happens in the background: 1. User pastes URL, clicks Request 2. Frontend POSTs to /api/request (Cloudflare Worker) 3. Worker creates the GitHub issue (triggers auto-add pipeline) 4. User sees inline success message with their docs link Changes: - Add worker/ with Cloudflare Worker that proxies to GitHub Issues API - Rewrite frontend JS to use fetch() with loading/success/error states - Remove nav "Request a Repo" button (the inline form is the entry point) - Remove .request-btn CSS, add .submit-feedback state classes Worker deployment: cd worker && npx wrangler secret put GITHUB_TOKEN npx wrangler deploy Route repos.supermodeltools.com/api/* to the worker in Cloudflare.
1 parent 676f876 commit a8512ea

File tree

3 files changed

+159
-30
lines changed

3 files changed

+159
-30
lines changed

generate-index.go

Lines changed: 59 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -196,18 +196,6 @@ a:focus-visible { outline: 2px solid var(--accent-light); outline-offset: 2px; b
196196
.site-nav { display: flex; gap: 16px; align-items: center; }
197197
.site-nav a { color: var(--text-muted); font-size: 14px; font-weight: 500; white-space: nowrap; }
198198
.site-nav a:hover { color: var(--text); text-decoration: none; }
199-
.request-btn {
200-
color: var(--accent-light) !important;
201-
border: 1px solid var(--accent);
202-
border-radius: var(--radius);
203-
padding: 6px 12px;
204-
transition: background 0.2s, color 0.2s;
205-
}
206-
.request-btn:hover {
207-
background: var(--accent);
208-
color: #fff !important;
209-
text-decoration: none !important;
210-
}
211199
.hero {
212200
padding: 64px 0 48px;
213201
text-align: center;
@@ -395,14 +383,21 @@ a:focus-visible { outline: 2px solid var(--accent-light); outline-offset: 2px; b
395383
pointer-events: auto;
396384
}
397385
.submit-btn.active:hover { background: var(--accent-light); }
398-
.submit-preview {
399-
margin-top: 8px;
386+
.submit-btn.loading {
387+
opacity: 0.6;
388+
pointer-events: none;
389+
}
390+
.submit-feedback {
391+
margin-top: 10px;
400392
font-size: 13px;
401-
color: var(--green);
402393
font-family: var(--mono);
403394
display: none;
404395
}
405-
.submit-preview.visible { display: block; }
396+
.submit-feedback.visible { display: block; }
397+
.submit-feedback.preview { color: var(--text-muted); }
398+
.submit-feedback.success { color: var(--green); }
399+
.submit-feedback.success a { color: var(--green); text-decoration: underline; }
400+
.submit-feedback.error { color: var(--red); }
406401
@media (max-width: 768px) {
407402
.container { padding: 0 16px; }
408403
.hero { padding: 40px 0 32px; }
@@ -432,7 +427,6 @@ a:focus-visible { outline: 2px solid var(--accent-light); outline-offset: 2px; b
432427
<a href="https://supermodeltools.com">Website</a>
433428
<a href="https://github.com/supermodeltools">GitHub</a>
434429
<a href="https://x.com/supermodeltools">X</a>
435-
<a href="https://github.com/supermodeltools/supermodeltools.github.io/issues/new?template=request-repo.yml" class="request-btn">+ Request a Repo</a>
436430
</nav>
437431
</div>
438432
</header>
@@ -464,7 +458,7 @@ a:focus-visible { outline: 2px solid var(--accent-light); outline-offset: 2px; b
464458
<input type="text" class="submit-input" id="submit-url" placeholder="https://github.com/owner/repo" autocomplete="off" spellcheck="false">
465459
<button class="submit-btn" id="submit-btn" type="button">Request</button>
466460
</div>
467-
<div class="submit-preview" id="submit-preview"></div>
461+
<div class="submit-feedback" id="submit-feedback"></div>
468462
</div>
469463
</div>
470464
@@ -510,10 +504,10 @@ a:focus-visible { outline: 2px solid var(--accent-light); outline-offset: 2px; b
510504
var noResults = document.getElementById('no-results');
511505
var submitInput = document.getElementById('submit-url');
512506
var submitBtn = document.getElementById('submit-btn');
513-
var submitPreview = document.getElementById('submit-preview');
507+
var feedback = document.getElementById('submit-feedback');
514508
var noResultsRequest = document.getElementById('no-results-request');
515509
516-
var issueBase = 'https://github.com/supermodeltools/supermodeltools.github.io/issues/new?template=request-repo.yml';
510+
var API_URL = '/api/request';
517511
518512
// --- Search ---
519513
searchInput.addEventListener('input', function() {
@@ -546,36 +540,71 @@ a:focus-visible { outline: 2px solid var(--accent-light); outline-offset: 2px; b
546540
return null;
547541
}
548542
543+
function showFeedback(msg, type) {
544+
feedback.className = 'submit-feedback visible ' + type;
545+
feedback.innerHTML = msg;
546+
}
547+
549548
submitInput.addEventListener('input', function() {
550549
var parsed = parseRepo(this.value);
551550
if (parsed) {
552551
var name = parsed.split('/')[1];
553-
submitPreview.textContent = '\u2192 Docs will be at repos.supermodeltools.com/' + name + '/';
554-
submitPreview.classList.add('visible');
552+
showFeedback('\u2192 repos.supermodeltools.com/' + name + '/', 'preview');
555553
submitBtn.classList.add('active');
556554
} else {
557-
submitPreview.classList.remove('visible');
555+
feedback.className = 'submit-feedback';
558556
submitBtn.classList.remove('active');
559557
}
560558
});
561559
562-
function submitRequest() {
560+
async function submitRequest() {
563561
var parsed = parseRepo(submitInput.value);
564562
if (!parsed) return;
563+
565564
var repoUrl = 'https://github.com/' + parsed;
566565
var name = parsed.split('/')[1];
567-
var url = issueBase
568-
+ '&repo_url=' + encodeURIComponent(repoUrl)
569-
+ '&title=' + encodeURIComponent('[Repo Request] ' + name);
570-
window.open(url, '_blank');
566+
567+
// Loading state
568+
submitBtn.classList.add('loading');
569+
submitBtn.textContent = 'Submitting...';
570+
showFeedback('Setting up ' + name + '...', 'preview');
571+
572+
try {
573+
var resp = await fetch(API_URL, {
574+
method: 'POST',
575+
headers: { 'Content-Type': 'application/json' },
576+
body: JSON.stringify({ url: repoUrl }),
577+
});
578+
var data = await resp.json();
579+
580+
if (!resp.ok || !data.success) {
581+
showFeedback(data.error || 'Something went wrong. Please try again.', 'error');
582+
submitBtn.classList.remove('loading');
583+
submitBtn.textContent = 'Request';
584+
return;
585+
}
586+
587+
// Success — show the link, clear the input
588+
showFeedback(
589+
'\u2713 Submitted! Docs will be generated at <a href="' + data.docs_url + '">' +
590+
'repos.supermodeltools.com/' + name + '/</a>', 'success'
591+
);
592+
submitInput.value = '';
593+
submitBtn.classList.remove('active', 'loading');
594+
submitBtn.textContent = 'Request';
595+
} catch (e) {
596+
showFeedback('Network error. Please try again.', 'error');
597+
submitBtn.classList.remove('loading');
598+
submitBtn.textContent = 'Request';
599+
}
571600
}
572601
573602
submitBtn.addEventListener('click', submitRequest);
574603
submitInput.addEventListener('keydown', function(e) {
575-
if (e.key === 'Enter') submitRequest();
604+
if (e.key === 'Enter' && submitBtn.classList.contains('active')) submitRequest();
576605
});
577606
578-
// "No results" request link: pre-fill with search query as a guess
607+
// "No results" link: scroll up and focus the submit input
579608
noResultsRequest.addEventListener('click', function() {
580609
var q = searchInput.value.trim();
581610
submitInput.value = q;

worker/index.js

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/**
2+
* Cloudflare Worker — repo request proxy
3+
*
4+
* Receives a repo URL from the homepage, creates a GitHub issue
5+
* (which triggers the auto-add-repo workflow), and returns immediately.
6+
*
7+
* Environment secrets (set via wrangler secret put):
8+
* GITHUB_TOKEN — fine-grained PAT with issues:write on supermodeltools.github.io
9+
*
10+
* Deploy:
11+
* cd worker && npx wrangler deploy
12+
*
13+
* Route (add in Cloudflare dashboard or wrangler.toml):
14+
* repos.supermodeltools.com/api/* → this worker
15+
*/
16+
17+
const CORS_HEADERS = {
18+
'Access-Control-Allow-Origin': 'https://repos.supermodeltools.com',
19+
'Access-Control-Allow-Methods': 'POST, OPTIONS',
20+
'Access-Control-Allow-Headers': 'Content-Type',
21+
};
22+
23+
const REPO_RE = /github\.com\/([a-zA-Z0-9._-]+\/[a-zA-Z0-9._-]+)/;
24+
25+
export default {
26+
async fetch(request, env) {
27+
if (request.method === 'OPTIONS') {
28+
return new Response(null, { status: 204, headers: CORS_HEADERS });
29+
}
30+
31+
if (request.method !== 'POST') {
32+
return jsonResponse({ error: 'Method not allowed' }, 405);
33+
}
34+
35+
let body;
36+
try {
37+
body = await request.json();
38+
} catch {
39+
return jsonResponse({ error: 'Invalid JSON' }, 400);
40+
}
41+
42+
const url = (body.url || '').trim().replace(/\/+$/, '').replace(/\.git$/, '');
43+
const match = url.match(REPO_RE);
44+
if (!match) {
45+
return jsonResponse({ error: 'Invalid GitHub repository URL' }, 400);
46+
}
47+
48+
const upstream = match[1];
49+
const name = upstream.split('/')[1];
50+
51+
// Create the GitHub issue — this triggers auto-add-repo.yml
52+
const ghResponse = await fetch(
53+
'https://api.github.com/repos/supermodeltools/supermodeltools.github.io/issues',
54+
{
55+
method: 'POST',
56+
headers: {
57+
Authorization: `Bearer ${env.GITHUB_TOKEN}`,
58+
Accept: 'application/vnd.github+json',
59+
'User-Agent': 'supermodel-request-bot',
60+
'Content-Type': 'application/json',
61+
},
62+
body: JSON.stringify({
63+
title: `[Repo Request] ${name}`,
64+
body: `### Repository URL\n\nhttps://github.com/${upstream}`,
65+
labels: ['repo-request'],
66+
}),
67+
}
68+
);
69+
70+
if (!ghResponse.ok) {
71+
const err = await ghResponse.text();
72+
console.error('GitHub API error:', ghResponse.status, err);
73+
return jsonResponse({ error: 'Failed to submit request. Please try again.' }, 502);
74+
}
75+
76+
const issue = await ghResponse.json();
77+
78+
return jsonResponse({
79+
success: true,
80+
name,
81+
upstream,
82+
docs_url: `https://repos.supermodeltools.com/${name}/`,
83+
issue_url: issue.html_url,
84+
});
85+
},
86+
};
87+
88+
function jsonResponse(data, status = 200) {
89+
return new Response(JSON.stringify(data), {
90+
status,
91+
headers: { 'Content-Type': 'application/json', ...CORS_HEADERS },
92+
});
93+
}

worker/wrangler.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
name = "repo-request"
2+
main = "index.js"
3+
compatibility_date = "2024-01-01"
4+
5+
# Route: intercept /api/* on the custom domain
6+
# Requires the domain to be proxied through Cloudflare
7+
# routes = [{ pattern = "repos.supermodeltools.com/api/*", zone_name = "supermodeltools.com" }]

0 commit comments

Comments
 (0)