fix: hide login key and sub-keys from sub-key sessions

This commit is contained in:
M1 2026-03-18 11:48:51 +04:00
parent c56af82053
commit eeb0318c4d
2 changed files with 32 additions and 14 deletions

View File

@ -72,13 +72,12 @@ function redirect(to: string) {
return new Response(null, { status: 302, headers: { Location: to } });
}
async function getAccountId(cookie: any, headers: any): Promise<string | null> {
async function getAccountId(cookie: any, headers: any): Promise<{ accountId: string; keyId: string | null } | null> {
const authHeader = headers["authorization"] ?? "";
const bearer = authHeader.match(/^bearer\s+(.+)$/i)?.[1]?.trim();
const key = cookie?.pingql_key?.value || bearer;
if (!key) return null;
const resolved = await resolveKey(key);
return resolved?.accountId ?? null;
return await resolveKey(key) ?? null;
}
const dashDir = resolve(import.meta.dir, "../dashboard");
@ -112,7 +111,9 @@ export const dashboard = new Elysia()
// Home — SSR monitor list
.get("/dashboard/home", async ({ cookie, headers }) => {
const accountId = await getAccountId(cookie, headers);
const resolved = await getAccountId(cookie, headers);
const accountId = resolved?.accountId ?? null;
const keyId = resolved?.keyId ?? null;
if (!accountId) return redirect("/dashboard");
const monitors = await sql`
@ -150,26 +151,33 @@ export const dashboard = new Elysia()
// Settings — SSR account info
.get("/dashboard/settings", async ({ cookie, headers }) => {
const accountId = await getAccountId(cookie, headers);
const resolved = await getAccountId(cookie, headers);
const accountId = resolved?.accountId ?? null;
const keyId = resolved?.keyId ?? null;
if (!accountId) return redirect("/dashboard");
const [acc] = await sql`SELECT id, email_hash, created_at FROM accounts WHERE id = ${accountId}`;
const apiKeys = await sql`SELECT id, key, label, created_at, last_used_at FROM api_keys WHERE account_id = ${accountId} ORDER BY created_at DESC`;
const loginKey = cookie?.pingql_key?.value ?? null;
const isSubKey = !!keyId;
const apiKeys = isSubKey ? [] : await sql`SELECT id, key, label, created_at, last_used_at FROM api_keys WHERE account_id = ${accountId} ORDER BY created_at DESC`;
const loginKey = isSubKey ? null : (cookie?.pingql_key?.value ?? null);
return html("settings", { nav: "settings", account: acc, apiKeys, accountId, loginKey });
return html("settings", { nav: "settings", account: acc, apiKeys, accountId, loginKey, isSubKey });
})
// New monitor
.get("/dashboard/monitors/new", async ({ cookie, headers }) => {
const accountId = await getAccountId(cookie, headers);
const resolved = await getAccountId(cookie, headers);
const accountId = resolved?.accountId ?? null;
const keyId = resolved?.keyId ?? null;
if (!accountId) return redirect("/dashboard");
return html("new", { nav: "monitors", scripts: ["/dashboard/query-builder.js"] });
})
// Home data endpoint for polling (monitor list change detection)
.get("/dashboard/home/data", async ({ cookie, headers }) => {
const accountId = await getAccountId(cookie, headers);
const resolved = await getAccountId(cookie, headers);
const accountId = resolved?.accountId ?? null;
const keyId = resolved?.keyId ?? null;
if (!accountId) return new Response(JSON.stringify({ error: "Unauthorized" }), { status: 401 });
const monitors = await sql`
@ -182,7 +190,9 @@ export const dashboard = new Elysia()
// Monitor detail — SSR with initial data
.get("/dashboard/monitors/:id", async ({ cookie, headers, params }) => {
const accountId = await getAccountId(cookie, headers);
const resolved = await getAccountId(cookie, headers);
const accountId = resolved?.accountId ?? null;
const keyId = resolved?.keyId ?? null;
if (!accountId) return redirect("/dashboard");
const [monitor] = await sql`
@ -200,7 +210,9 @@ export const dashboard = new Elysia()
// Chart partial endpoint — returns just the latency chart SVG
.get("/dashboard/monitors/:id/chart", async ({ cookie, headers, params }) => {
const accountId = await getAccountId(cookie, headers);
const resolved = await getAccountId(cookie, headers);
const accountId = resolved?.accountId ?? null;
const keyId = resolved?.keyId ?? null;
if (!accountId) return new Response("Unauthorized", { status: 401 });
const [monitor] = await sql`
@ -220,7 +232,9 @@ export const dashboard = new Elysia()
// Sparkline partial — returns just the SVG for one monitor
.get("/dashboard/monitors/:id/sparkline", async ({ cookie, headers, params }) => {
const accountId = await getAccountId(cookie, headers);
const resolved = await getAccountId(cookie, headers);
const accountId = resolved?.accountId ?? null;
const keyId = resolved?.keyId ?? null;
if (!accountId) return new Response("Unauthorized", { status: 401 });
const [monitor] = await sql`

View File

@ -14,6 +14,7 @@
<section class="bg-gray-900 rounded-xl border border-gray-800 p-6">
<h2 class="text-sm font-semibold text-gray-300 mb-4">Account</h2>
<div class="space-y-3">
<% if (!it.isSubKey) { %>
<div>
<label class="block text-xs text-gray-500 mb-1">Login Key</label>
<div class="flex gap-2">
@ -22,6 +23,7 @@
<button onclick="confirmReset()" class="px-3 bg-gray-800 hover:bg-gray-700 border border-red-900/50 hover:border-red-700/50 text-red-400 rounded-lg text-xs transition-colors">Rotate</button>
</div>
</div>
<% } %>
<div>
<label class="block text-xs text-gray-500 mb-1">Member since</label>
<p id="created-at" class="text-sm text-gray-400"><%= createdDate %></p>
@ -45,7 +47,8 @@
<!-- Sub-keys -->
<!-- Sub-keys (hidden for sub-key sessions) -->
<% if (!it.isSubKey) { %>
<section class="bg-gray-900 rounded-xl border border-gray-800 p-6">
<div class="flex items-center justify-between mb-4">
<div>
@ -87,6 +90,7 @@
<% } %>
</div>
</section>
<% } %>
</main>