The code and data behind xeiaso.net
5
fork

Configure Feed

Select the types of activity you want to include in your feed.

refactor(sponsor-panel): namespace HTMX routes under /api/htmx/

Move the three HTMX form endpoints (invite, logo, thoth-token) behind
an /api/htmx/ prefix so they are visibly distinct from OAuth and page
routes. Updates handlers, dashboard template, godoc, and the endpoints
table in docs/README.md.

Assisted-by: Claude Opus 4.7 via Claude Code
Signed-off-by: Xe Iaso <me@xeiaso.net>

Xe Iaso 537aa15c 5cdf11a4

+19 -18
+3 -2
cmd/sponsor-panel/docs/README.md
··· 131 131 | `/login` | GET | No | Initiate GitHub OAuth | 132 132 | `/callback` | GET | No | OAuth callback, create session | 133 133 | `/` | GET | Yes | Dashboard | 134 - | `/invite` | POST | Yes | Invite user to team | 135 - | `/logo` | POST | Yes | Submit logo (create issue) | 134 + | `/api/htmx/invite` | POST | Yes | Invite user to team | 135 + | `/api/htmx/logo` | POST | Yes | Submit logo (create issue) | 136 + | `/api/htmx/thoth-token` | POST | Yes | Issue Thoth JWT | 136 137 137 138 --- 138 139
+3 -3
cmd/sponsor-panel/handlers.go
··· 21 21 "xeiaso.net/v4/cmd/sponsor-panel/templates" 22 22 ) 23 23 24 - // inviteHandler handles POST /invite - invites a user to the GitHub team. 24 + // inviteHandler handles POST /api/htmx/invite - invites a user to the GitHub team. 25 25 func (s *Server) inviteHandler(w http.ResponseWriter, r *http.Request) { 26 26 if r.Method != http.MethodPost { 27 27 http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) ··· 105 105 renderInviteSuccess(w, username, state) 106 106 } 107 107 108 - // logoHandler handles POST /logo - submits a logo to a GitHub issue. 108 + // logoHandler handles POST /api/htmx/logo - submits a logo to a GitHub issue. 109 109 func (s *Server) logoHandler(w http.ResponseWriter, r *http.Request) { 110 110 if r.Method != http.MethodPost { 111 111 http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) ··· 322 322 templates.LogoSuccess(company, issueURL, issueNumber).Render(context.Background(), w) 323 323 } 324 324 325 - // thothTokenHandler handles POST /thoth-token - issues a Thoth JWT for the user. 325 + // thothTokenHandler handles POST /api/htmx/thoth-token - issues a Thoth JWT for the user. 326 326 func (s *Server) thothTokenHandler(w http.ResponseWriter, r *http.Request) { 327 327 if r.Method != http.MethodPost { 328 328 http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
+7 -7
cmd/sponsor-panel/main.go
··· 320 320 // Dashboard handler (also serves login page if not authenticated) 321 321 mux.HandleFunc("/", server.dashboardHandler) 322 322 323 - // Feature handlers 324 - mux.HandleFunc("/invite", server.inviteHandler) 325 - mux.HandleFunc("/logo", server.logoHandler) 326 - mux.HandleFunc("/thoth-token", server.thothTokenHandler) 323 + // Feature handlers (HTMX API) 324 + mux.HandleFunc("/api/htmx/invite", server.inviteHandler) 325 + mux.HandleFunc("/api/htmx/logo", server.logoHandler) 326 + mux.HandleFunc("/api/htmx/thoth-token", server.thothTokenHandler) 327 327 328 328 // Expose Prometheus metrics at /metrics for observability 329 329 mux.Handle("/metrics", promhttp.Handler()) ··· 336 336 "/logout", 337 337 "/login-page", 338 338 "/", 339 - "/invite", 340 - "/logo", 341 - "/thoth-token", 339 + "/api/htmx/invite", 340 + "/api/htmx/logo", 341 + "/api/htmx/thoth-token", 342 342 "/metrics", 343 343 }) 344 344
+3 -3
cmd/sponsor-panel/templates/dashboard.templ
··· 206 206 <p class="panel-sub">Invite team members to TecharoHQ as part of your sponsorship.</p> 207 207 </div> 208 208 </div> 209 - <form hx-post="/invite" hx-target="#invite-result" class="space-y-4"> 209 + <form hx-post="/api/htmx/invite" hx-target="#invite-result" class="space-y-4"> 210 210 <label class="field-label" for="invite-username">GitHub username</label> 211 211 <input 212 212 id="invite-username" ··· 241 241 <p class="panel-sub">Submit your company logo for the Anubis README. SVG preferred; PNG at 512×512 accepted.</p> 242 242 </div> 243 243 </div> 244 - <form hx-post="/logo" hx-encoding="multipart/form-data" hx-target="#logo-result" class="space-y-4"> 244 + <form hx-post="/api/htmx/logo" hx-encoding="multipart/form-data" hx-target="#logo-result" class="space-y-4"> 245 245 <div class="grid-2"> 246 246 <div> 247 247 <label class="field-label" for="logo-company">Company name</label> ··· 296 296 </div> 297 297 </dl> 298 298 <div class="btn-row"> 299 - <form hx-post="/thoth-token" hx-target="#thoth-result"> 299 + <form hx-post="/api/htmx/thoth-token" hx-target="#thoth-result"> 300 300 <button type="submit" class="btn btn-accent" hx-disabled-elt="this"> 301 301 <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> 302 302 <path d="M20 11A8.1 8.1 0 0 0 4.5 9M4 5v4h4M4 13a8.1 8.1 0 0 0 15.5 2M20 19v-4h-4"></path>
+3 -3
cmd/sponsor-panel/templates/dashboard_templ.go
··· 402 402 templ_7745c5c3_Var13 = templ.NopComponent 403 403 } 404 404 ctx = templ.ClearChildren(ctx) 405 - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "<div class=\"panel-head\"><div class=\"panel-head-icon\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M9 7a4 4 0 1 0 8 0 4 4 0 0 0-8 0M3 21v-2a4 4 0 0 1 4-4h4a4 4 0 0 1 4 4v2M16 3.13a4 4 0 0 1 0 7.75M21 21v-2a4 4 0 0 0-3-3.85\"></path></svg></div><div><h2 class=\"panel-title\">Team Invitations</h2><p class=\"panel-sub\">Invite team members to TecharoHQ as part of your sponsorship.</p></div></div><form hx-post=\"/invite\" hx-target=\"#invite-result\" class=\"space-y-4\"><label class=\"field-label\" for=\"invite-username\">GitHub username</label> <input id=\"invite-username\" type=\"text\" name=\"username\" placeholder=\"octocat\" autocomplete=\"off\" required class=\"input\"><div class=\"btn-row\"><button type=\"submit\" class=\"btn btn-primary\"><svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M5 12h14M12 5v14\"></path></svg> Send Invitation</button></div></form><div id=\"invite-result\"></div>") 405 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "<div class=\"panel-head\"><div class=\"panel-head-icon\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M9 7a4 4 0 1 0 8 0 4 4 0 0 0-8 0M3 21v-2a4 4 0 0 1 4-4h4a4 4 0 0 1 4 4v2M16 3.13a4 4 0 0 1 0 7.75M21 21v-2a4 4 0 0 0-3-3.85\"></path></svg></div><div><h2 class=\"panel-title\">Team Invitations</h2><p class=\"panel-sub\">Invite team members to TecharoHQ as part of your sponsorship.</p></div></div><form hx-post=\"/api/htmx/invite\" hx-target=\"#invite-result\" class=\"space-y-4\"><label class=\"field-label\" for=\"invite-username\">GitHub username</label> <input id=\"invite-username\" type=\"text\" name=\"username\" placeholder=\"octocat\" autocomplete=\"off\" required class=\"input\"><div class=\"btn-row\"><button type=\"submit\" class=\"btn btn-primary\"><svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M5 12h14M12 5v14\"></path></svg> Send Invitation</button></div></form><div id=\"invite-result\"></div>") 406 406 if templ_7745c5c3_Err != nil { 407 407 return templ_7745c5c3_Err 408 408 } ··· 431 431 templ_7745c5c3_Var14 = templ.NopComponent 432 432 } 433 433 ctx = templ.ClearChildren(ctx) 434 - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "<div class=\"panel-head\"><div class=\"panel-head-icon panel-head-icon--purple\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M15 8h.01M12.5 21H6a3 3 0 0 1-3-3V6a3 3 0 0 1 3-3h12a3 3 0 0 1 3 3v6.5M3 16l5-5c.928-.893 2.072-.893 3 0l5 5M14 14l1-1c.679-.653 1.473-.829 2.214-.526\"></path></svg></div><div><h2 class=\"panel-title\">Logo Submission</h2><p class=\"panel-sub\">Submit your company logo for the Anubis README. SVG preferred; PNG at 512×512 accepted.</p></div></div><form hx-post=\"/logo\" hx-encoding=\"multipart/form-data\" hx-target=\"#logo-result\" class=\"space-y-4\"><div class=\"grid-2\"><div><label class=\"field-label\" for=\"logo-company\">Company name</label> <input id=\"logo-company\" type=\"text\" name=\"company\" placeholder=\"Techaro Inc.\" required class=\"input\"></div><div><label class=\"field-label\" for=\"logo-website\">Website URL</label> <input id=\"logo-website\" type=\"url\" name=\"website\" placeholder=\"https://techaro.lol\" required class=\"input\"></div></div><div><label class=\"field-label\" for=\"logo-file\">Logo file</label> <label class=\"drop-zone\" for=\"logo-file\" id=\"logo-drop\"><svg width=\"32\" height=\"32\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M7 18a4.6 4.4 0 0 1 0-9 5 4.5 0 0 1 11 2h1a3.5 3.5 0 0 1 0 7h-1M9 15l3-3l3 3M12 12v9\"></path></svg> <strong id=\"logo-drop-name\">Drop your logo here, or click to browse</strong> <span class=\"drop-hint\">PNG, JPEG, or SVG · up to 5 MB</span></label> <input id=\"logo-file\" type=\"file\" name=\"logo\" accept=\"image/png,image/jpeg,image/svg+xml\" required class=\"sr-only\"></div><div class=\"btn-row\"><button type=\"submit\" class=\"btn btn-dark\">Submit Logo</button> <button type=\"reset\" class=\"btn btn-ghost\" id=\"logo-reset\">Clear</button></div></form><div id=\"logo-result\"></div>") 434 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "<div class=\"panel-head\"><div class=\"panel-head-icon panel-head-icon--purple\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M15 8h.01M12.5 21H6a3 3 0 0 1-3-3V6a3 3 0 0 1 3-3h12a3 3 0 0 1 3 3v6.5M3 16l5-5c.928-.893 2.072-.893 3 0l5 5M14 14l1-1c.679-.653 1.473-.829 2.214-.526\"></path></svg></div><div><h2 class=\"panel-title\">Logo Submission</h2><p class=\"panel-sub\">Submit your company logo for the Anubis README. SVG preferred; PNG at 512×512 accepted.</p></div></div><form hx-post=\"/api/htmx/logo\" hx-encoding=\"multipart/form-data\" hx-target=\"#logo-result\" class=\"space-y-4\"><div class=\"grid-2\"><div><label class=\"field-label\" for=\"logo-company\">Company name</label> <input id=\"logo-company\" type=\"text\" name=\"company\" placeholder=\"Techaro Inc.\" required class=\"input\"></div><div><label class=\"field-label\" for=\"logo-website\">Website URL</label> <input id=\"logo-website\" type=\"url\" name=\"website\" placeholder=\"https://techaro.lol\" required class=\"input\"></div></div><div><label class=\"field-label\" for=\"logo-file\">Logo file</label> <label class=\"drop-zone\" for=\"logo-file\" id=\"logo-drop\"><svg width=\"32\" height=\"32\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M7 18a4.6 4.4 0 0 1 0-9 5 4.5 0 0 1 11 2h1a3.5 3.5 0 0 1 0 7h-1M9 15l3-3l3 3M12 12v9\"></path></svg> <strong id=\"logo-drop-name\">Drop your logo here, or click to browse</strong> <span class=\"drop-hint\">PNG, JPEG, or SVG · up to 5 MB</span></label> <input id=\"logo-file\" type=\"file\" name=\"logo\" accept=\"image/png,image/jpeg,image/svg+xml\" required class=\"sr-only\"></div><div class=\"btn-row\"><button type=\"submit\" class=\"btn btn-dark\">Submit Logo</button> <button type=\"reset\" class=\"btn btn-ghost\" id=\"logo-reset\">Clear</button></div></form><div id=\"logo-result\"></div>") 435 435 if templ_7745c5c3_Err != nil { 436 436 return templ_7745c5c3_Err 437 437 } ··· 460 460 templ_7745c5c3_Var15 = templ.NopComponent 461 461 } 462 462 ctx = templ.ClearChildren(ctx) 463 - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "<div class=\"panel-head\"><div class=\"panel-head-icon panel-head-icon--yellow\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M8 15a4 4 0 1 0 0-8 4 4 0 0 0 0 8ZM10.85 12.15 21 22l-2 2-1.5-1.5L17 22l-1.5-1.5L14 22l-4-4 1-1-1-1\"></path></svg></div><div><h2 class=\"panel-title\">Thoth API Token</h2><p class=\"panel-sub\">Generate an API token for <a href=\"https://anubis.techaro.lol/docs/admin/thoth\" class=\"underline\">Thoth</a>. Use it to enable IP, ASN and country code checks in your Anubis deployment.</p></div></div><dl class=\"meta-list\"><div><dt>Scope</dt><dd>read:asn, read:geoip, read:ip</dd></div><div><dt>Endpoint</dt><dd><code>thoth.techaro.lol:443</code></dd></div></dl><div class=\"btn-row\"><form hx-post=\"/thoth-token\" hx-target=\"#thoth-result\"><button type=\"submit\" class=\"btn btn-accent\" hx-disabled-elt=\"this\"><svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M20 11A8.1 8.1 0 0 0 4.5 9M4 5v4h4M4 13a8.1 8.1 0 0 0 15.5 2M20 19v-4h-4\"></path></svg> Generate Token</button></form></div><div id=\"thoth-result\"></div>") 463 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "<div class=\"panel-head\"><div class=\"panel-head-icon panel-head-icon--yellow\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M8 15a4 4 0 1 0 0-8 4 4 0 0 0 0 8ZM10.85 12.15 21 22l-2 2-1.5-1.5L17 22l-1.5-1.5L14 22l-4-4 1-1-1-1\"></path></svg></div><div><h2 class=\"panel-title\">Thoth API Token</h2><p class=\"panel-sub\">Generate an API token for <a href=\"https://anubis.techaro.lol/docs/admin/thoth\" class=\"underline\">Thoth</a>. Use it to enable IP, ASN and country code checks in your Anubis deployment.</p></div></div><dl class=\"meta-list\"><div><dt>Scope</dt><dd>read:asn, read:geoip, read:ip</dd></div><div><dt>Endpoint</dt><dd><code>thoth.techaro.lol:443</code></dd></div></dl><div class=\"btn-row\"><form hx-post=\"/api/htmx/thoth-token\" hx-target=\"#thoth-result\"><button type=\"submit\" class=\"btn btn-accent\" hx-disabled-elt=\"this\"><svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M20 11A8.1 8.1 0 0 0 4.5 9M4 5v4h4M4 13a8.1 8.1 0 0 0 15.5 2M20 19v-4h-4\"></path></svg> Generate Token</button></form></div><div id=\"thoth-result\"></div>") 464 464 if templ_7745c5c3_Err != nil { 465 465 return templ_7745c5c3_Err 466 466 }