story-research-zapwall/lib/accessControl.ts

108 lines
3.0 KiB
TypeScript

/**
* Access control rules for objects
*
* Rules:
* - Only the author (pubkey) who published the original note can modify or delete it
* - All users have read access to public content (previews, metadata)
* - For paid content, users must have paid (via zap receipt) to access the full content
* - Payment verification follows the transaction rules (zap receipt validation)
*/
import type { Event } from 'nostr-tools'
import { extractTagsFromEvent } from './nostrTagSystem'
import { canModifyObject } from './versionManager'
import { t } from './i18n'
/**
* Check if a user can modify an object
* Only the author (pubkey) who published the original note can modify it
*/
export function canUserModify(event: Event, userPubkey: string): boolean {
return canModifyObject(event, userPubkey)
}
/**
* Check if a user can delete an object
* Only the author (pubkey) who published the original note can delete it
*/
export function canUserDelete(event: Event, userPubkey: string): boolean {
return canModifyObject(event, userPubkey)
}
/**
* Check if a user can read an object
* All users can read public content (previews, metadata)
* For paid content, users must have paid (via zap receipt) to access full content
*/
export function canUserRead(event: Event, _userPubkey: string | null, hasPaid: boolean = false): {
canReadPreview: boolean
canReadFullContent: boolean
} {
const tags = extractTagsFromEvent(event)
// Preview is always readable (public)
const canReadPreview = true
// Full content access depends on paywall status
if (tags.paywall) {
// Paid content: user must have paid
const canReadFullContent = hasPaid
return { canReadPreview, canReadFullContent }
}
// Free content: everyone can read
return { canReadPreview, canReadFullContent: true }
}
/**
* Check if content is paid (has paywall tag)
*/
export function isPaidContent(event: Event): boolean {
const tags = extractTagsFromEvent(event)
return tags.paywall === true
}
/**
* Access control result
*/
export interface AccessControlResult {
canModify: boolean
canDelete: boolean
canReadPreview: boolean
canReadFullContent: boolean
isPaid: boolean
reason?: string
}
/**
* Get complete access control information for an object
*/
export function getAccessControl(
event: Event,
userPubkey: string | null,
hasPaid: boolean = false
): AccessControlResult {
const canModify = userPubkey ? canUserModify(event, userPubkey) : false
const canDelete = userPubkey ? canUserDelete(event, userPubkey) : false
const { canReadPreview, canReadFullContent } = canUserRead(event, userPubkey, hasPaid)
const isPaid = isPaidContent(event)
let reason: string | undefined
if (isPaid && !canReadFullContent) {
reason = t('access.paymentRequired')
} else if (!canModify && userPubkey) {
reason = t('access.onlyAuthorModify')
} else if (!canDelete && userPubkey) {
reason = t('access.onlyAuthorDelete')
}
return {
canModify,
canDelete,
canReadPreview,
canReadFullContent,
isPaid,
...(reason ? { reason } : {}),
}
}