107 lines
3.0 KiB
TypeScript
107 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'
|
|
|
|
/**
|
|
* 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 = 'Payment required to access full content'
|
|
} else if (!canModify && userPubkey) {
|
|
reason = 'Only the author can modify this object'
|
|
} else if (!canDelete && userPubkey) {
|
|
reason = 'Only the author can delete this object'
|
|
}
|
|
|
|
return {
|
|
canModify,
|
|
canDelete,
|
|
canReadPreview,
|
|
canReadFullContent,
|
|
isPaid,
|
|
...(reason ? { reason } : {}),
|
|
}
|
|
}
|