2026-07-28 16:16:26 +00:00
|
|
|
import {derived, readable, writable} from "svelte/store"
|
|
|
|
|
import {ago, MINUTE, now, simpleCache} from "@welshman/lib"
|
|
|
|
|
import {ROOM_CREATE_PERMISSION, hexTags, tagValues} from "@welshman/util"
|
2026-09-17 17:26:40 +00:00
|
|
|
import {Relays} from "@welshman/app"
|
2026-09-12 08:31:37 +00:00
|
|
|
import {fromApp, relayManagement, user} from "@app/core"
|
2026-07-28 16:16:26 +00:00
|
|
|
import {deriveEventsForUrl} from "@app/repository"
|
|
|
|
|
|
|
|
|
|
export type BannedPubkeyItem = {
|
|
|
|
|
pubkey: string
|
|
|
|
|
reason: string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const deriveSpaceBannedPubkeyItems = (url: string) => {
|
|
|
|
|
const store = writable<BannedPubkeyItem[]>([])
|
|
|
|
|
|
|
|
|
|
relayManagement
|
|
|
|
|
.get()
|
|
|
|
|
.forUrl(url)
|
|
|
|
|
.listBannedPubkeys()
|
|
|
|
|
.then(({result = []}) => store.set(result))
|
|
|
|
|
|
|
|
|
|
return store
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-12 08:31:37 +00:00
|
|
|
const deriveSupportedMethodsForPubkey = simpleCache(([, url]: [pubkey: string, url: string]) => {
|
2026-07-28 16:16:26 +00:00
|
|
|
let checkedAt = 0
|
|
|
|
|
|
|
|
|
|
return readable<string[]>([], set => {
|
2026-09-12 08:31:37 +00:00
|
|
|
if (checkedAt < ago(5, MINUTE)) {
|
2026-07-28 16:16:26 +00:00
|
|
|
checkedAt = now()
|
|
|
|
|
|
|
|
|
|
relayManagement
|
|
|
|
|
.get()
|
|
|
|
|
.forUrl(url)
|
|
|
|
|
.supportedMethods()
|
|
|
|
|
.then(({result = []}) => set(result))
|
|
|
|
|
.catch(error => {
|
|
|
|
|
checkedAt = 0
|
|
|
|
|
console.error(error)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
2026-09-12 08:31:37 +00:00
|
|
|
// The request is signed as the logged in user and answered for that pubkey, so the methods
|
|
|
|
|
// belong to a user as much as to a url and logging in has to swap them out.
|
|
|
|
|
export const deriveSpaceSupportedMethods = (url?: string) =>
|
|
|
|
|
fromApp($app => {
|
|
|
|
|
if (url && $app.user) {
|
|
|
|
|
return deriveSupportedMethodsForPubkey($app.user.pubkey, url)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return readable<string[]>([])
|
|
|
|
|
})
|
|
|
|
|
|
2026-07-28 16:16:26 +00:00
|
|
|
// User
|
|
|
|
|
|
2026-09-18 08:42:42 +00:00
|
|
|
// Holding any management method at all is what makes someone staff. Every control the relay
|
|
|
|
|
// answers for is gated on its own method instead, so this is only for the room-level permissions
|
|
|
|
|
// NIP-86 has no method for.
|
|
|
|
|
export const deriveUserIsSpaceStaff = (url?: string) =>
|
2026-07-28 16:16:26 +00:00
|
|
|
derived(deriveSpaceSupportedMethods(url), $methods => $methods.length > 0)
|
|
|
|
|
|
2026-09-17 17:26:40 +00:00
|
|
|
// The one identity a space names as its own, in its NIP-11 `pubkey`. Space-wide content with no
|
|
|
|
|
// author to scope it to belongs to that person.
|
|
|
|
|
export const deriveUserIsSpaceOwner = (url: string) =>
|
|
|
|
|
derived(
|
|
|
|
|
[user, fromApp($app => $app.use(Relays).one(url))],
|
|
|
|
|
([$user, $relay]) => $user.pubkey === $relay?.pubkey,
|
|
|
|
|
)
|
|
|
|
|
|
2026-07-28 16:16:26 +00:00
|
|
|
export const deriveUserCanCreateRoom = (url: string) =>
|
|
|
|
|
derived(
|
|
|
|
|
[
|
|
|
|
|
user,
|
|
|
|
|
deriveEventsForUrl(url, [{kinds: [ROOM_CREATE_PERMISSION]}]),
|
2026-09-18 08:42:42 +00:00
|
|
|
deriveUserIsSpaceStaff(url),
|
2026-07-28 16:16:26 +00:00
|
|
|
],
|
2026-09-18 08:42:42 +00:00
|
|
|
([$user, $events, $isStaff]) =>
|
|
|
|
|
$isStaff || $events.some(event => tagValues(hexTags("p"), event.tags).includes($user.pubkey)),
|
2026-07-28 16:16:26 +00:00
|
|
|
)
|