Skip to content

Commit b6dc419

Browse files
Remove Cloudflare Worker, use client-side GitHub API
Replace the Cloudflare Worker proxy with direct GitHub API calls from the browser using a fine-grained PAT (issues:write scope). The token is injected at build time via ISSUES_TOKEN env var. - Delete worker/ directory (index.js, wrangler.toml) - Generate static skeleton page at /generating/?repo=name - Frontend JS calls GitHub API directly to create issues - build-index.yml passes ISSUES_TOKEN secret to generator - Add .gitignore to exclude site/ build artifacts
1 parent 21776a3 commit b6dc419

File tree

5 files changed

+209
-448
lines changed

5 files changed

+209
-448
lines changed

.github/workflows/build-index.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ jobs:
2929

3030
- name: Generate site
3131
run: go run generate-index.go
32+
env:
33+
ISSUES_TOKEN: ${{ secrets.ISSUES_TOKEN }}
3234

3335
- uses: actions/upload-pages-artifact@v3
3436
with:

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
site/

generate-index.go

Lines changed: 206 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@ func main() {
6464
os.Exit(1)
6565
}
6666

67+
if err := generateSkeleton(); err != nil {
68+
fmt.Fprintf(os.Stderr, "Error generating skeleton: %v\n", err)
69+
os.Exit(1)
70+
}
71+
6772
// Copy CNAME and static root files to site directory
6873
if cname, err := os.ReadFile("CNAME"); err == nil {
6974
os.WriteFile("site/CNAME", cname, 0644)
@@ -86,6 +91,13 @@ func generate404() error {
8691
`), 0644)
8792
}
8893

94+
func generateSkeleton() error {
95+
if err := os.MkdirAll("site/generating", 0755); err != nil {
96+
return err
97+
}
98+
return os.WriteFile("site/generating/index.html", []byte(skeletonTemplate), 0644)
99+
}
100+
89101
func generateSitemap(cfg Config) error {
90102
var b strings.Builder
91103
b.WriteString(`<?xml version="1.0" encoding="UTF-8"?>` + "\n")
@@ -99,6 +111,11 @@ func generateSitemap(cfg Config) error {
99111
return os.WriteFile("site/sitemap.xml", []byte(b.String()), 0644)
100112
}
101113

114+
type PageData struct {
115+
Config
116+
Token string
117+
}
118+
102119
func generateIndex(cfg Config) error {
103120
tmpl, err := template.New("index").Funcs(template.FuncMap{
104121
"escape": html.EscapeString,
@@ -133,7 +150,7 @@ func generateIndex(cfg Config) error {
133150
}
134151
defer f.Close()
135152

136-
return tmpl.Execute(f, cfg)
153+
return tmpl.Execute(f, PageData{Config: cfg, Token: os.Getenv("ISSUES_TOKEN")})
137154
}
138155

139156
const indexTemplate = `<!DOCTYPE html>
@@ -519,7 +536,8 @@ a:focus-visible { outline: 2px solid var(--accent-light); outline-offset: 2px; b
519536
var feedback = document.getElementById('submit-feedback');
520537
var noResultsRequest = document.getElementById('no-results-request');
521538
522-
var API_URL = '/api/request';
539+
var GH_TOKEN = '{{.Token}}';
540+
var GH_REPO = 'supermodeltools/supermodeltools.github.io';
523541
524542
// --- Search ---
525543
searchInput.addEventListener('input', function() {
@@ -573,6 +591,11 @@ a:focus-visible { outline: 2px solid var(--accent-light); outline-offset: 2px; b
573591
var parsed = parseRepo(submitInput.value);
574592
if (!parsed) return;
575593
594+
if (!GH_TOKEN) {
595+
showFeedback('Generate is not configured yet.', 'error');
596+
return;
597+
}
598+
576599
var repoUrl = 'https://github.com/' + parsed;
577600
var name = parsed.split('/')[1];
578601
@@ -582,22 +605,30 @@ a:focus-visible { outline: 2px solid var(--accent-light); outline-offset: 2px; b
582605
showFeedback('Setting up ' + name + '...', 'preview');
583606
584607
try {
585-
var resp = await fetch(API_URL, {
608+
var resp = await fetch('https://api.github.com/repos/' + GH_REPO + '/issues', {
586609
method: 'POST',
587-
headers: { 'Content-Type': 'application/json' },
588-
body: JSON.stringify({ url: repoUrl }),
610+
headers: {
611+
'Authorization': 'Bearer ' + GH_TOKEN,
612+
'Accept': 'application/vnd.github+json',
613+
'Content-Type': 'application/json',
614+
},
615+
body: JSON.stringify({
616+
title: '[Repo Request] ' + name,
617+
body: '### Repository URL\n\n' + repoUrl,
618+
labels: ['repo-request'],
619+
}),
589620
});
590-
var data = await resp.json();
591621
592-
if (!resp.ok || !data.success) {
593-
showFeedback(data.error || 'Something went wrong. Please try again.', 'error');
622+
if (!resp.ok) {
623+
var err = await resp.json().catch(function() { return {}; });
624+
showFeedback(err.message || 'Something went wrong. Please try again.', 'error');
594625
submitBtn.classList.remove('loading');
595626
submitBtn.textContent = 'Generate';
596627
return;
597628
}
598629
599-
// Redirect to the skeleton loading page — served by the worker
600-
window.location.href = data.generating_url;
630+
// Redirect to the skeleton loading page
631+
window.location.href = '/generating/?repo=' + encodeURIComponent(name);
601632
} catch (e) {
602633
showFeedback('Network error. Please try again.', 'error');
603634
submitBtn.classList.remove('loading');
@@ -624,3 +655,168 @@ a:focus-visible { outline: 2px solid var(--accent-light); outline-offset: 2px; b
624655
</html>
625656
`
626657

658+
const skeletonTemplate = `<!DOCTYPE html>
659+
<html lang="en">
660+
<head>
661+
<meta charset="utf-8">
662+
<meta name="viewport" content="width=device-width, initial-scale=1">
663+
<title>Generating — Architecture Documentation</title>
664+
<link rel="preconnect" href="https://fonts.googleapis.com">
665+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
666+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
667+
<style>
668+
:root{--bg:#0f1117;--bg-card:#1a1d27;--bg-hover:#22263a;--border:#2a2e3e;--text:#e4e4e7;--text-muted:#9ca3af;--accent:#6366f1;--accent-light:#818cf8;--font:'Inter',-apple-system,BlinkMacSystemFont,sans-serif;--mono:'JetBrains Mono','Fira Code',monospace;--max-w:1200px;--radius:8px}
669+
*{margin:0;padding:0;box-sizing:border-box}html{overflow-x:hidden}
670+
body{font-family:var(--font);background:var(--bg);color:var(--text);line-height:1.6;-webkit-font-smoothing:antialiased;overflow-x:hidden}
671+
a{color:var(--accent-light);text-decoration:none}a:hover{text-decoration:underline}
672+
.container{max-width:var(--max-w);margin:0 auto;padding:0 24px}
673+
.site-header{border-bottom:1px solid var(--border);padding:16px 0;position:sticky;top:0;background:var(--bg);z-index:100}
674+
.site-header .container{display:flex;align-items:center;justify-content:space-between;gap:16px}
675+
.site-brand{font-size:18px;font-weight:700;color:var(--text);display:flex;align-items:center;gap:8px;white-space:nowrap}
676+
.site-brand:hover{text-decoration:none;color:var(--accent-light)}
677+
.site-brand svg{width:24px;height:24px}
678+
.site-nav{display:flex;gap:16px;align-items:center}
679+
.site-nav a,.site-nav span{color:var(--text-muted);font-size:14px;font-weight:500;white-space:nowrap}
680+
.nav-all-repos{color:var(--accent-light)!important;padding-right:12px;margin-right:4px;border-right:1px solid var(--border)}
681+
.hero{padding:48px 0 40px;text-align:center}
682+
.hero h1{font-size:28px;font-weight:700;margin-bottom:12px}
683+
.hero-sub{color:var(--text-muted);font-size:15px;max-width:560px;margin:0 auto 24px}
684+
.hero-actions{display:flex;gap:8px;justify-content:center;margin-bottom:16px}
685+
.hero-btn{display:inline-flex;align-items:center;gap:6px;padding:7px 14px;font-size:13px;font-weight:500;background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius);color:var(--text-muted)}
686+
.hero-stats{display:flex;justify-content:center;gap:28px;flex-wrap:wrap}
687+
.hero-stat{text-align:center}
688+
.hero-stat .label{font-size:12px;color:var(--text-muted)}
689+
@keyframes shimmer{0%{background-position:-400px 0}100%{background-position:400px 0}}
690+
.shim{background:linear-gradient(90deg,var(--bg-card) 25%,var(--bg-hover) 50%,var(--bg-card) 75%);background-size:800px 100%;animation:shimmer 1.8s ease-in-out infinite;border-radius:4px}
691+
.shim-num{width:48px;height:28px;margin:0 auto 4px;border-radius:4px}
692+
.chart-panel{background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius);padding:24px;margin-bottom:24px}
693+
.chart-panel h3{font-size:16px;font-weight:600;margin-bottom:16px}
694+
.shim-chart{height:280px;border-radius:var(--radius)}
695+
.section{margin-bottom:40px}
696+
.section-title{font-size:20px;font-weight:700;margin-bottom:12px}
697+
.tax-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:8px}
698+
.tax-entry-skel{display:flex;align-items:center;justify-content:space-between;padding:10px 14px;background:var(--bg-card);border:1px solid var(--border);border-radius:6px}
699+
.shim-entry-name{width:60%;height:14px}
700+
.shim-entry-count{width:28px;height:14px}
701+
.gen-status{position:fixed;bottom:24px;left:50%;transform:translateX(-50%);background:var(--bg-card);border:1px solid var(--border);border-radius:12px;padding:14px 24px;display:flex;align-items:center;gap:14px;font-size:14px;color:var(--text);box-shadow:0 8px 32px rgba(0,0,0,0.4);z-index:200;max-width:90vw}
702+
.gen-spinner{width:18px;height:18px;flex-shrink:0;border:2px solid var(--border);border-top-color:var(--accent-light);border-radius:50%;animation:spin .8s linear infinite}
703+
@keyframes spin{to{transform:rotate(360deg)}}
704+
.gen-step{color:var(--text-muted)}.gen-step strong{color:var(--text)}
705+
.site-footer{border-top:1px solid var(--border);padding:32px 0;margin-top:48px;color:var(--text-muted);font-size:13px;text-align:center}
706+
@media(max-width:768px){.container{padding:0 16px}.hero{padding:32px 0 24px}.hero h1{font-size:22px}.hero-stats{gap:16px}.tax-grid{grid-template-columns:1fr}.gen-status{bottom:12px;padding:10px 16px;font-size:13px}}
707+
</style>
708+
</head>
709+
<body>
710+
<header class="site-header">
711+
<div class="container">
712+
<span class="site-brand" id="brand">
713+
<svg viewBox="0 0 90 78" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M90 61.1124C75.9375 73.4694 59.8419 78 44.7554 78C29.669 78 11.8614 72.6122 0 61.1011V16.9458C11.6168 6 29.891 0 44.9887 0C62.77 0 78.8723 6.97959 89.9887 16.9458V61.1124H90ZM88.1881 38.9553C77.7923 22.8824 59.8983 15.7959 44.7554 15.7959C29.6126 15.7959 13.4515 21.9008 1.556 38.9444C12.5382 54.69 26.9 62.5085 44.7554 62.0944C67.6297 61.5639 77.6495 51.9184 88.1881 38.9553ZM44.7554 16.3475C32.4756 16.3475 22.3888 26.6879 22.2554 38.9388C34.3765 38.9162 44.7554 29.1429 44.7554 16.3475C44.7554 29.1429 55.1344 38.9162 67.2554 38.9388C67.1202 26.5216 57.1141 16.3475 44.7554 16.3475ZM44.7554 61.5639C44.7554 48.4898 34.3765 38.9613 22.2554 38.9388C22.3888 51.1897 32.4756 61.5639 44.7554 61.5639C57.0352 61.5639 67.122 51.1897 67.2554 38.9388C55.1344 38.9613 44.7554 48.4898 44.7554 61.5639Z" fill="currentColor"/></svg>
714+
<span id="brand-name"></span>
715+
</span>
716+
<nav class="site-nav">
717+
<a href="https://repos.supermodeltools.com/" class="nav-all-repos">&larr; All Repos</a>
718+
<span>By Type</span><span>Domains</span><span>Languages</span><span>Tags</span>
719+
</nav>
720+
</div>
721+
</header>
722+
<main>
723+
<div class="container">
724+
<div class="hero">
725+
<h1 id="hero-title"></h1>
726+
<div class="hero-actions">
727+
<span class="hero-btn">View on GitHub</span>
728+
<span class="hero-btn">Star</span>
729+
<span class="hero-btn">Fork</span>
730+
</div>
731+
<p class="hero-sub">Architecture documentation generated from code analysis. Explore every file, function, class, and domain.</p>
732+
<div class="hero-stats">
733+
<div class="hero-stat"><div class="shim shim-num"></div><div class="label">Total Entities</div></div>
734+
<div class="hero-stat"><div class="shim shim-num"></div><div class="label">Node Types</div></div>
735+
<div class="hero-stat"><div class="shim shim-num"></div><div class="label">Languages</div></div>
736+
<div class="hero-stat"><div class="shim shim-num"></div><div class="label">Domains</div></div>
737+
<div class="hero-stat"><div class="shim shim-num"></div><div class="label">Subdomains</div></div>
738+
<div class="hero-stat"><div class="shim shim-num"></div><div class="label">Top Directories</div></div>
739+
</div>
740+
</div>
741+
<div class="chart-panel"><h3>Architecture Overview</h3><div class="shim shim-chart"></div></div>
742+
<div class="chart-panel"><h3>Codebase Composition</h3><div class="shim shim-chart" style="height:200px"></div></div>
743+
<div class="section"><h2 class="section-title">Node Types</h2><div class="tax-grid">
744+
<div class="tax-entry-skel"><div class="shim shim-entry-name"></div><div class="shim shim-entry-count"></div></div>
745+
<div class="tax-entry-skel"><div class="shim shim-entry-name"></div><div class="shim shim-entry-count"></div></div>
746+
<div class="tax-entry-skel"><div class="shim shim-entry-name"></div><div class="shim shim-entry-count"></div></div>
747+
<div class="tax-entry-skel"><div class="shim shim-entry-name"></div><div class="shim shim-entry-count"></div></div>
748+
<div class="tax-entry-skel"><div class="shim shim-entry-name"></div><div class="shim shim-entry-count"></div></div>
749+
<div class="tax-entry-skel"><div class="shim shim-entry-name"></div><div class="shim shim-entry-count"></div></div>
750+
</div></div>
751+
<div class="section"><h2 class="section-title">Domains</h2><div class="tax-grid">
752+
<div class="tax-entry-skel"><div class="shim shim-entry-name"></div><div class="shim shim-entry-count"></div></div>
753+
<div class="tax-entry-skel"><div class="shim shim-entry-name"></div><div class="shim shim-entry-count"></div></div>
754+
<div class="tax-entry-skel"><div class="shim shim-entry-name"></div><div class="shim shim-entry-count"></div></div>
755+
<div class="tax-entry-skel"><div class="shim shim-entry-name"></div><div class="shim shim-entry-count"></div></div>
756+
</div></div>
757+
<div class="section"><h2 class="section-title">Languages</h2><div class="tax-grid">
758+
<div class="tax-entry-skel"><div class="shim shim-entry-name"></div><div class="shim shim-entry-count"></div></div>
759+
<div class="tax-entry-skel"><div class="shim shim-entry-name"></div><div class="shim shim-entry-count"></div></div>
760+
<div class="tax-entry-skel"><div class="shim shim-entry-name"></div><div class="shim shim-entry-count"></div></div>
761+
</div></div>
762+
</div>
763+
</main>
764+
<footer class="site-footer"><div class="container"><p>Generated with <a href="https://github.com/supermodeltools/arch-docs">arch-docs</a> by <a href="https://supermodeltools.com">supermodeltools</a></p></div></footer>
765+
<div class="gen-status" id="gen-status">
766+
<div class="gen-spinner"></div>
767+
<div class="gen-step" id="gen-step"><strong>Generating docs</strong> &mdash; forking repository&hellip;</div>
768+
</div>
769+
<script>
770+
(function() {
771+
var params = new URLSearchParams(window.location.search);
772+
var name = params.get('repo') || 'repository';
773+
var docsUrl = 'https://repos.supermodeltools.com/' + encodeURIComponent(name) + '/';
774+
775+
document.getElementById('brand-name').textContent = name;
776+
document.getElementById('hero-title').textContent = name;
777+
document.title = 'Generating ' + name + ' \u2014 Architecture Documentation';
778+
779+
var statusEl = document.getElementById('gen-step');
780+
var messages = [
781+
{ text: '<strong>Generating docs</strong> \u2014 forking repository\u2026', at: 0 },
782+
{ text: '<strong>Generating docs</strong> \u2014 analyzing codebase\u2026', at: 8000 },
783+
{ text: '<strong>Generating docs</strong> \u2014 building code graphs\u2026', at: 35000 },
784+
{ text: '<strong>Generating docs</strong> \u2014 mapping architecture\u2026', at: 60000 },
785+
{ text: '<strong>Generating docs</strong> \u2014 deploying site\u2026', at: 90000 },
786+
{ text: '<strong>Almost there</strong> \u2014 finalizing\u2026', at: 120000 },
787+
];
788+
messages.forEach(function(m) {
789+
setTimeout(function() { statusEl.innerHTML = m.text; }, m.at);
790+
});
791+
792+
var pollCount = 0;
793+
var maxPolls = 120;
794+
function poll() {
795+
pollCount++;
796+
if (pollCount > maxPolls) {
797+
statusEl.innerHTML = '<strong>Still working</strong> \u2014 this repo may be large. <a href="' + docsUrl + '">Check manually \u2192</a>';
798+
return;
799+
}
800+
fetch(docsUrl, { cache: 'no-store', redirect: 'follow' })
801+
.then(function(resp) {
802+
if (resp.ok) {
803+
return resp.text().then(function(html) {
804+
if (html.indexOf('arch-docs') !== -1 && html.indexOf('gen-status') === -1) {
805+
statusEl.innerHTML = '<strong>Ready!</strong> Loading docs\u2026';
806+
setTimeout(function() { window.location.href = docsUrl; }, 600);
807+
return;
808+
}
809+
setTimeout(poll, 5000);
810+
});
811+
}
812+
setTimeout(poll, 5000);
813+
})
814+
.catch(function() { setTimeout(poll, 5000); });
815+
}
816+
setTimeout(poll, 5000);
817+
})();
818+
</script>
819+
</body>
820+
</html>
821+
`
822+

0 commit comments

Comments
 (0)