101 lines · 5.1 KB
import type { TokenPermissions } from "../token-scope";
import { TOKEN_AREAS } from "../token-scope";

const AREA_LABELS: Record<(typeof TOKEN_AREAS)[number], string> = {
  contents: "Repository contents (files, branches, commits)",
  issues: "Issues and comments",
  pulls: "Pull requests and reviews",
  actions: "CI runs and logs",
  admin: "Repository administration",
};

function escapeHtml(value: string): string {
  return value
    .replaceAll("&", "&amp;")
    .replaceAll("<", "&lt;")
    .replaceAll(">", "&gt;")
    .replaceAll('"', "&quot;")
    .replaceAll("'", "&#39;");
}

const STYLE = `body{font-family:"IBM Plex Sans",system-ui,sans-serif;background:#0d1117;color:#e6edf3;margin:0;display:flex;justify-content:center}
main{max-width:34rem;margin:4rem 1rem;padding:2rem;border:1px solid #30363d;border-radius:12px;background:#161b22}
h1{font-size:1.25rem;margin:0 0 .5rem}p,li{line-height:1.5;color:#c9d1d9}code{font-family:"IBM Plex Mono",monospace;font-size:.85em}
ul{padding-left:1.25rem}.warn{color:#f0b72f}.muted{color:#8b949e;font-size:.875rem}
.actions{display:flex;gap:.75rem;margin-top:1.5rem}button{font:inherit;padding:.5rem 1rem;border-radius:8px;border:1px solid #30363d;cursor:pointer}
.approve{background:#238636;color:#fff;border-color:#2ea043}.deny{background:#21262d;color:#e6edf3}a{color:#58a6ff}`;

function page(title: string, body: string, status = 200): Response {
  const html = `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>${escapeHtml(title)}</title><style>${STYLE}</style></head><body><main>${body}</main></body></html>`;
  return new Response(html, {
    status,
    headers: {
      "content-type": "text/html; charset=utf-8",
      "cache-control": "no-store",
      "content-security-policy": "default-src 'none'; style-src 'unsafe-inline'; frame-ancestors 'none'",
      "x-frame-options": "DENY",
    },
  });
}