Add identity endpoint

This commit is contained in:
Jon Staab
2026-03-26 16:10:24 -07:00
parent 9da5e027a7
commit a2f9ca9688
9 changed files with 129 additions and 38 deletions
+11 -4
View File
@@ -1,8 +1,8 @@
import { A, useLocation } from "@solidjs/router"
import { A, useLocation, useNavigate } from "@solidjs/router"
import { createEffect, createMemo, createSignal, For, onCleanup, Show } from "solid-js"
import Fuse from "fuse.js"
import { eventStore, primeProfiles, useActiveAccount, useProfilePicture } from "../lib/nostr"
import { useAdminCheck, useTenantRelays, type Relay } from "../lib/hooks"
import { useIdentity, useTenantRelays, type Relay } from "../lib/hooks"
import serverIcon from "../assets/server.svg"
import Modal from "./Modal"
@@ -32,15 +32,16 @@ function RelayIcon() {
export default function AppShell(props: { children?: any }) {
const location = useLocation()
const navigate = useNavigate()
const account = useActiveAccount()
const picture = useProfilePicture(() => account()?.pubkey)
const [adminCheck] = useAdminCheck(() => account()?.id)
const [identity] = useIdentity(() => account()?.pubkey)
const [tenantRelays] = useTenantRelays()
const [profile, setProfile] = createSignal<Profile>({})
const [searchOpen, setSearchOpen] = createSignal(false)
const [searchQuery, setSearchQuery] = createSignal("")
const isAdmin = createMemo(() => !!adminCheck()?.is_admin)
const isAdmin = createMemo(() => !!identity()?.is_admin)
const username = createMemo(() => profile().name || profile().display_name || shortenPubkey(account()?.pubkey))
const nip05 = createMemo(() => profile().nip05 || "No NIP-05")
const searchedRelays = createMemo<Relay[]>(() => {
@@ -82,6 +83,12 @@ export default function AppShell(props: { children?: any }) {
})
})
createEffect(() => {
const currentIdentity = identity()
if (!currentIdentity || currentIdentity.is_admin || currentIdentity.is_tenant || location.pathname === "/") return
navigate("/", { replace: true })
})
const myResources = [{ href: "/relays", label: "My Relays" }]
const adminResources = [
{ href: "/admin/tenants", label: "Tenants" },
+10
View File
@@ -71,6 +71,12 @@ export type Invoice = {
period_end: number
}
export type Identity = {
pubkey: string
is_admin: boolean
is_tenant: boolean
}
export type CreateRelayInput = {
tenant?: string
subdomain: string
@@ -168,6 +174,10 @@ export function listPlans() {
return callApi<undefined, Plan[]>("GET", "/plans")
}
export function getIdentity() {
return callApi<undefined, Identity>("GET", "/identity")
}
export function getPlan(id: string) {
return callApi<undefined, Plan>("GET", `/plans/${id}`)
}
+38 -24
View File
@@ -1,10 +1,9 @@
import { createResource } from "solid-js"
import { Relay as NostrRelay, RelayManagement } from "applesauce-relay"
import {
ApiError,
createRelay,
createTenant,
deactivateRelay,
getIdentity,
getRelay,
getTenant,
listRelays,
@@ -14,31 +13,61 @@ import {
updateRelay,
updateTenantBilling,
type CreateRelayInput,
type Identity,
type Relay,
type Tenant,
type UpdateRelayInput,
} from "./api"
import { getActivePubkey, getActiveSigner } from "./nostr"
type IdentityCache = {
pubkey: string
value: Identity
}
let identityCache: IdentityCache | undefined
let identityInflight: Promise<Identity> | undefined
let identityInflightPubkey: string | undefined
function requireActivePubkey() {
const pubkey = getActivePubkey()
if (!pubkey) throw new Error("Not logged in")
return pubkey
}
async function ensureActiveTenant() {
try {
await createTenant()
} catch (e) {
if (e instanceof ApiError && e.status === 422) return
throw e
async function loadIdentity(pubkey?: string) {
if (!pubkey) throw new Error("Not logged in")
if (identityCache?.pubkey === pubkey) {
return identityCache.value
}
if (identityInflight && identityInflightPubkey === pubkey) {
return identityInflight
}
const request = getIdentity().then((identity) => {
identityCache = { pubkey, value: identity }
return identity
}).finally(() => {
if (identityInflightPubkey === pubkey) {
identityInflight = undefined
identityInflightPubkey = undefined
}
})
identityInflight = request
identityInflightPubkey = pubkey
return request
}
export function useIdentity(source: () => string | undefined) {
return createResource(source, loadIdentity)
}
export function useTenant() {
return createResource(async () => {
const pubkey = requireActivePubkey()
await ensureActiveTenant()
return getTenant(pubkey)
})
}
@@ -46,7 +75,6 @@ export function useTenant() {
export function useTenantRelays() {
return createResource(async () => {
const pubkey = requireActivePubkey()
await ensureActiveTenant()
return listTenantRelays(pubkey)
})
}
@@ -54,7 +82,6 @@ export function useTenantRelays() {
export function useTenantInvoices() {
return createResource(async () => {
const pubkey = requireActivePubkey()
await ensureActiveTenant()
return listTenantInvoices(pubkey)
})
}
@@ -78,21 +105,8 @@ export function useAdminTenantDetail(pubkey: () => string) {
})
}
export function useAdminCheck(source: () => string | undefined) {
return createResource(source, async () => {
try {
await listTenants()
return { is_admin: true }
} catch (e) {
if (e instanceof ApiError && e.status === 403) return { is_admin: false }
throw e
}
})
}
export async function createRelayForActiveTenant(input: CreateRelayInput) {
const pubkey = requireActivePubkey()
await ensureActiveTenant()
return createRelay({ ...input, tenant: pubkey })
}