Barazo default frontend barazo.forum
at main 917 lines 19 kB view raw
1/** 2 * API response types matching barazo-api schemas. 3 * These mirror the Zod-validated responses from the API. 4 * @see ~/Documents/Git/singi-labs/barazo-api/src/routes/ 5 */ 6 7// Re-export lexicon types for AT Protocol record validation and constants. 8// API response types below are enriched by the AppView (computed fields like 9// replyCount, indexedAt, etc.) and differ from the raw PDS record shapes. 10export type { 11 TopicPostInput, 12 TopicReplyInput, 13 ReactionInput, 14 ActorPreferencesInput, 15} from '@singi-labs/barazo-lexicons' 16 17export { 18 LEXICON_IDS, 19 topicPostSchema, 20 topicReplySchema, 21 reactionSchema, 22 actorPreferencesSchema, 23} from '@singi-labs/barazo-lexicons' 24 25// --- Categories --- 26 27export interface Category { 28 id: string 29 slug: string 30 name: string 31 description: string | null 32 parentId: string | null 33 sortOrder: number 34 communityDid: string 35 maturityRating: MaturityRating 36 createdAt: string 37 updatedAt: string 38} 39 40export interface CategoryTreeNode extends Category { 41 children: CategoryTreeNode[] 42} 43 44export interface CategoryWithTopicCount extends Category { 45 topicCount: number 46} 47 48export interface CategoriesResponse { 49 categories: CategoryTreeNode[] 50} 51 52// --- Pages --- 53 54export type PageStatus = 'draft' | 'published' 55 56export interface Page { 57 id: string 58 slug: string 59 title: string 60 content: string 61 status: PageStatus 62 metaDescription: string | null 63 parentId: string | null 64 sortOrder: number 65 communityDid: string 66 createdAt: string 67 updatedAt: string 68} 69 70export interface PageTreeNode extends Page { 71 children: PageTreeNode[] 72} 73 74export interface PagesResponse { 75 pages: PageTreeNode[] 76} 77 78export interface CreatePageInput { 79 title: string 80 slug: string 81 content?: string 82 status?: PageStatus 83 metaDescription?: string | null 84 parentId?: string | null 85 sortOrder?: number 86} 87 88export interface UpdatePageInput { 89 title?: string 90 slug?: string 91 content?: string 92 status?: PageStatus 93 metaDescription?: string | null 94 parentId?: string | null 95 sortOrder?: number 96} 97 98// --- Author Profile (enriched by AppView) --- 99 100export interface AuthorProfile { 101 did: string 102 handle: string 103 displayName: string | null 104 avatarUrl: string | null 105} 106 107// --- Topics --- 108 109export interface Topic { 110 uri: string 111 rkey: string 112 authorDid: string 113 author?: AuthorProfile 114 title: string 115 content: string 116 category: string 117 site: string | null 118 tags: string[] | null 119 communityDid: string 120 cid: string 121 replyCount: number 122 reactionCount: number 123 isAuthorDeleted: boolean 124 isModDeleted: boolean 125 isPinned: boolean 126 isLocked: boolean 127 pinnedScope: 'category' | 'forum' | null 128 pinnedAt: string | null 129 categoryMaturityRating: MaturityRating 130 lastActivityAt: string 131 publishedAt: string 132 indexedAt: string 133} 134 135export interface TopicsResponse { 136 topics: Topic[] 137 cursor: string | null 138} 139 140export interface CreateTopicInput { 141 title: string 142 content: string 143 category: string 144 tags?: string[] 145 crossPostBluesky?: boolean 146 crossPostFrontpage?: boolean 147} 148 149export interface UpdateTopicInput { 150 title?: string 151 content?: string 152 category?: string 153 tags?: string[] 154} 155 156export type ModerationStatus = 'approved' | 'held' 157 158/** Slim response from POST /api/topics (differs from full Topic) */ 159export interface CreateTopicResponse { 160 uri: string 161 cid: string 162 rkey: string 163 title: string 164 category: string 165 authorHandle: string 166 moderationStatus: ModerationStatus 167 publishedAt: string 168} 169 170// --- Replies --- 171 172export interface Reply { 173 uri: string 174 rkey: string 175 authorDid: string 176 author?: AuthorProfile 177 content: string 178 rootUri: string 179 rootCid: string 180 parentUri: string 181 parentCid: string 182 communityDid: string 183 cid: string 184 depth: number 185 childCount?: number 186 reactionCount: number 187 isAuthorDeleted: boolean 188 isModDeleted: boolean 189 createdAt: string 190 indexedAt: string 191} 192 193export interface RepliesResponse { 194 replies: Reply[] 195 cursor: string | null 196} 197 198export interface CreateReplyInput { 199 content: string 200 parentUri?: string 201 labels?: string[] 202} 203 204export interface UpdateReplyInput { 205 content: string 206 labels?: string[] 207} 208 209/** Slim response from POST /api/topics/:uri/replies (differs from full Reply) */ 210export interface CreateReplyResponse { 211 uri: string 212 cid: string 213 rkey: string 214 moderationStatus: ModerationStatus 215 createdAt: string 216} 217 218// --- Reactions --- 219 220export interface Reaction { 221 uri: string 222 rkey: string 223 authorDid: string 224 subjectUri: string 225 subjectCid: string 226 type: string 227 communityDid: string 228 cid: string 229 createdAt: string 230} 231 232export interface ReactionsResponse { 233 reactions: Reaction[] 234 cursor: string | null 235} 236 237// --- Search --- 238 239export interface SearchResult { 240 type: 'topic' | 'reply' 241 uri: string 242 rkey: string 243 authorDid: string 244 authorHandle: string | null 245 title: string | null 246 content: string 247 category: string | null 248 communityDid: string 249 replyCount: number | null 250 reactionCount: number 251 publishedAt: string 252 rank: number 253 rootUri: string | null 254 rootTitle: string | null 255} 256 257export interface SearchResponse { 258 results: SearchResult[] 259 cursor: string | null 260 total: number 261 searchMode: 'fulltext' | 'hybrid' 262} 263 264// --- Community --- 265 266export interface CommunitySettings { 267 id: string 268 initialized: boolean 269 communityDid: string | null 270 adminDid: string | null 271 communityName: string 272 maturityRating: MaturityRating 273 reactionSet: string[] 274 communityDescription: string | null 275 communityLogoUrl: string | null 276 faviconUrl: string | null 277 headerLogoUrl: string | null 278 showCommunityName: boolean 279 primaryColor: string | null 280 accentColor: string | null 281 jurisdictionCountry: string | null 282 ageThreshold: number 283 maxReplyDepth: number 284 requireLoginForMature: boolean 285 createdAt: string 286 updatedAt: string 287} 288 289export interface PublicSettings { 290 communityDid: string | null 291 communityName: string 292 maturityRating: MaturityRating 293 maxReplyDepth: number 294 communityDescription: string | null 295 communityLogoUrl: string | null 296 faviconUrl: string | null 297 headerLogoUrl: string | null 298 showCommunityName: boolean 299} 300 301export interface CommunityStats { 302 topicCount: number 303 replyCount: number 304 userCount: number 305 categoryCount: number 306 reportCount: number 307 recentTopics: number 308 recentReplies: number 309 recentUsers: number 310} 311 312// --- Auth --- 313 314export type UserRole = 'user' | 'moderator' | 'admin' 315 316export interface AuthSession { 317 accessToken: string 318 expiresAt: string 319 did: string 320 handle: string 321 displayName: string | null 322 avatarUrl: string | null 323 role: UserRole 324 crossPostScopesGranted?: boolean 325} 326 327export interface AuthUser { 328 did: string 329 handle: string 330 displayName: string | null 331 avatarUrl: string | null 332 role: UserRole 333} 334 335// --- Notifications --- 336 337export type NotificationType = 'reply' | 'reaction' | 'mention' | 'moderation' 338 339export interface Notification { 340 id: string 341 type: NotificationType 342 userDid: string 343 actorDid: string 344 actorHandle: string 345 subjectUri: string 346 subjectTitle: string | null 347 subjectAuthorDid: string 348 subjectAuthorHandle: string | null 349 message: string 350 read: boolean 351 createdAt: string 352} 353 354export interface NotificationsResponse { 355 notifications: Notification[] 356 cursor: string | null 357 unreadCount: number 358} 359 360// --- Moderation --- 361 362export type ReportReasonType = 363 | 'spam' 364 | 'sexual' 365 | 'harassment' 366 | 'violation' 367 | 'misleading' 368 | 'other' 369 370export type ReportResolution = 'dismissed' | 'warned' | 'labeled' | 'removed' | 'banned' 371 372export interface ModerationReport { 373 id: string 374 reporterDid: string 375 reporterHandle: string 376 targetUri: string 377 targetAuthorDid: string 378 targetAuthorHandle: string 379 targetContent: string 380 targetTitle: string | null 381 reasonType: ReportReasonType 382 reason: string | null 383 potentiallyIllegal: boolean 384 status: 'pending' | 'resolved' 385 resolution: ReportResolution | null 386 resolvedAt: string | null 387 resolvedByDid: string | null 388 communityDid: string 389 createdAt: string 390} 391 392export interface ModerationReportsResponse { 393 reports: ModerationReport[] 394 cursor: string | null 395 total: number 396} 397 398// --- User's own reports (my-reports) --- 399 400export type AppealStatus = 'none' | 'pending' | 'rejected' 401 402export interface MyReport { 403 id: number 404 reporterDid: string 405 targetUri: string 406 targetDid: string 407 reasonType: ReportReasonType 408 description: string | null 409 status: 'pending' | 'resolved' 410 resolutionType: ReportResolution | null 411 resolvedBy: string | null 412 resolvedAt: string | null 413 appealReason: string | null 414 appealedAt: string | null 415 appealStatus: AppealStatus 416 createdAt: string 417} 418 419export interface MyReportsResponse { 420 reports: MyReport[] 421 cursor: string | null 422} 423 424export interface FirstPostQueueItem { 425 id: string 426 authorDid: string 427 authorHandle: string 428 contentUri: string 429 contentType: 'topic' | 'reply' 430 title: string | null 431 content: string 432 accountAge: string 433 crossCommunityCount: number 434 bannedFromOtherCommunities: number 435 status: 'pending' | 'approved' | 'rejected' 436 communityDid: string 437 createdAt: string 438} 439 440export interface FirstPostQueueResponse { 441 items: FirstPostQueueItem[] 442 cursor: string | null 443 total: number 444} 445 446export type ModerationActionType = 447 | 'lock' 448 | 'unlock' 449 | 'pin' 450 | 'unpin' 451 | 'delete' 452 | 'ban' 453 | 'unban' 454 | 'warn' 455 | 'label' 456 | 'approve' 457 | 'reject' 458 459export interface ModerationLogEntry { 460 id: string 461 actionType: ModerationActionType 462 moderatorDid: string 463 moderatorHandle: string 464 targetUri: string | null 465 targetDid: string | null 466 targetHandle: string | null 467 reason: string | null 468 communityDid: string 469 createdAt: string 470} 471 472export interface ModerationLogResponse { 473 actions: ModerationLogEntry[] 474 cursor: string | null 475} 476 477export interface ModerationThresholds { 478 autoBlockReportCount: number 479 warnThreshold: number 480 firstPostQueueCount: number 481 newAccountRateLimit: number 482 linkPostingHold: boolean 483 topicCreationDelay: boolean 484 burstDetectionPostCount: number 485 burstDetectionMinutes: number 486} 487 488export interface ReportedUser { 489 did: string 490 handle: string 491 reportCount: number 492 latestReportAt: string 493 bannedFromOtherCommunities: number 494} 495 496export interface ReportedUsersResponse { 497 users: ReportedUser[] 498} 499 500// --- Admin Users --- 501 502export interface AdminUser { 503 did: string 504 handle: string 505 displayName: string | null 506 avatarUrl: string | null 507 role: 'member' | 'moderator' | 'admin' 508 isBanned: boolean 509 bannedAt: string | null 510 banReason: string | null 511 bannedFromOtherCommunities: number 512 topicCount: number 513 replyCount: number 514 reportCount: number 515 firstSeenAt: string 516 lastActiveAt: string 517} 518 519export interface AdminUsersResponse { 520 users: AdminUser[] 521 cursor: string | null 522 total: number 523} 524 525// --- Plugins --- 526 527export type PluginSource = 'core' | 'official' | 'community' | 'experimental' 528 529export interface PluginSettingsSchema { 530 [key: string]: { 531 type: 'boolean' | 'string' | 'number' | 'select' 532 label: string 533 description?: string 534 default: boolean | string | number 535 options?: string[] 536 } 537} 538 539export interface Plugin { 540 id: string 541 name: string 542 displayName: string 543 version: string 544 description: string 545 source: PluginSource 546 enabled: boolean 547 category: string 548 dependencies: string[] 549 dependents: string[] 550 settingsSchema: PluginSettingsSchema 551 settings: Record<string, boolean | string | number> 552 installedAt: string 553} 554 555export interface PluginsResponse { 556 plugins: Plugin[] 557} 558 559export interface RegistryPlugin { 560 name: string 561 displayName: string 562 description: string 563 version: string 564 source: PluginSource 565 category: string 566 barazoVersion: string 567 author: { name: string; url?: string } 568 license: string 569 npmUrl: string 570 repositoryUrl?: string 571 approved: boolean 572 featured: boolean 573 downloads: number 574} 575 576export interface RegistrySearchResponse { 577 plugins: RegistryPlugin[] 578} 579 580// --- User Preferences --- 581 582export interface UserPreferences { 583 maturityLevel: 'sfw' | 'mature' 584 declaredAge: number | null 585 mutedWords: string[] 586 blockedDids: string[] 587 blockedProfiles: AuthorProfile[] 588 mutedDids: string[] 589 crossPostBluesky: boolean 590 crossPostFrontpage: boolean 591 updatedAt: string 592} 593 594export interface UpdatePreferencesInput { 595 maturityLevel?: 'sfw' | 'mature' 596 mutedWords?: string[] 597 blockedDids?: string[] 598 mutedDids?: string[] 599 crossPostBluesky?: boolean 600 crossPostFrontpage?: boolean 601} 602 603// --- Per-Community Preference Overrides --- 604 605export interface CommunityPreferenceOverride { 606 communityDid: string 607 communityName: string 608 maturityLevel: 'inherit' | 'sfw' | 'mature' 609 mutedWords: string[] 610 blockedDids: string[] 611 blockedProfiles: AuthorProfile[] 612} 613 614export interface CommunityPreferencesResponse { 615 communities: CommunityPreferenceOverride[] 616} 617 618export interface UpdateCommunityPreferenceInput { 619 maturityLevel?: 'inherit' | 'sfw' | 'mature' 620 mutedWords?: string[] 621 blockedDids?: string[] 622} 623 624export interface AgeDeclarationResponse { 625 success: boolean 626 declaredAge: number 627} 628 629// --- Setup --- 630 631export type SetupStatus = { initialized: false } | { initialized: true; communityName: string } 632 633export interface InitializeCommunityInput { 634 communityName?: string 635} 636 637export interface InitializeResponse { 638 initialized: true 639 adminDid: string 640 communityName: string 641 communityDid?: string 642} 643 644// --- Onboarding Fields --- 645 646export type OnboardingFieldType = 647 | 'age_confirmation' 648 | 'tos_acceptance' 649 | 'newsletter_email' 650 | 'custom_text' 651 | 'custom_select' 652 | 'custom_checkbox' 653 654export type OnboardingFieldSource = 'platform' | 'admin' 655 656export type HostingMode = 'saas' | 'selfhosted' 657 658export interface OnboardingField { 659 id: string 660 communityDid: string 661 fieldType: OnboardingFieldType 662 label: string 663 description: string | null 664 isMandatory: boolean 665 sortOrder: number 666 source: OnboardingFieldSource 667 config: Record<string, unknown> | null 668 createdAt: string 669 updatedAt: string 670} 671 672export interface AdminOnboardingFieldsResponse { 673 fields: OnboardingField[] 674 hostingMode: HostingMode 675} 676 677export interface CreateOnboardingFieldInput { 678 fieldType: OnboardingFieldType 679 label: string 680 description?: string 681 isMandatory?: boolean 682 sortOrder?: number 683 config?: Record<string, unknown> 684} 685 686export interface UpdateOnboardingFieldInput { 687 label?: string 688 description?: string | null 689 isMandatory?: boolean 690 sortOrder?: number 691 config?: Record<string, unknown> | null 692} 693 694export interface OnboardingStatus { 695 complete: boolean 696 fields: OnboardingField[] 697 responses: Record<string, unknown> 698 missingFields: Array<{ id: string; label: string; fieldType: OnboardingFieldType }> 699} 700 701export interface SubmitOnboardingInput { 702 responses: Array<{ fieldId: string; response: unknown }> 703} 704 705// --- User Profile (public, with community resolution) --- 706 707export interface UserProfile { 708 did: string 709 handle: string 710 displayName: string | null 711 avatarUrl: string | null 712 bannerUrl: string | null 713 bio: string | null 714 role: string 715 firstSeenAt: string 716 lastActiveAt: string 717 accountCreatedAt: string | null 718 followersCount: number 719 followsCount: number 720 atprotoPostsCount: number 721 hasBlueskyProfile: boolean 722 communityCount: number 723 labels: Array<{ 724 val: string 725 src: string 726 isSelfLabel: boolean 727 }> 728 activity: { 729 topicCount: number 730 replyCount: number 731 reactionsReceived: number 732 votesReceived: number 733 } 734 globalActivity?: { 735 topicCount: number 736 replyCount: number 737 reactionsReceived: number 738 votesReceived: number 739 } 740} 741 742// --- Community Profile (own profile in a community) --- 743 744export interface CommunityProfile { 745 did: string 746 handle: string 747 displayName: string | null 748 avatarUrl: string | null 749 bannerUrl: string | null 750 bio: string | null 751 communityDid: string 752 hasOverride: boolean 753 source: { 754 displayName: string | null 755 avatarUrl: string | null 756 bannerUrl: string | null 757 bio: string | null 758 } 759} 760 761export interface UpdateCommunityProfileInput { 762 displayName?: string | null 763 bio?: string | null 764} 765 766export interface UploadResponse { 767 url: string 768} 769 770// --- Sybil Detection --- 771 772export type SybilClusterStatus = 'flagged' | 'dismissed' | 'monitoring' | 'banned' 773 774export interface SybilCluster { 775 id: number 776 clusterHash: string 777 memberCount: number 778 internalEdgeCount: number 779 externalEdgeCount: number 780 suspicionRatio: number 781 status: SybilClusterStatus 782 detectedAt: string 783 reviewedBy: string | null 784 reviewedAt: string | null 785} 786 787export interface ClusterMember { 788 did: string 789 handle: string 790 displayName: string 791 roleInCluster: 'core' | 'peripheral' 792 trustScore: number 793 reputationScore: number 794 accountAge: string 795 communitiesActiveIn: number 796} 797 798export interface SybilClusterDetail extends SybilCluster { 799 members: ClusterMember[] 800} 801 802export interface SybilClustersResponse { 803 clusters: SybilCluster[] 804} 805 806export interface TrustSeed { 807 id: number 808 did: string 809 handle: string 810 displayName: string 811 communityId: string | null 812 reason: string | null 813 implicit: boolean 814 createdAt: string 815} 816 817export interface TrustSeedsResponse { 818 seeds: TrustSeed[] 819} 820 821/** The API accepts a handle and resolves it to a DID server-side. */ 822export interface CreateTrustSeedInput { 823 handle: string 824 communityId?: string 825 reason?: string 826} 827 828export interface PdsTrustFactor { 829 pdsHost: string 830 trustFactor: number 831 isDefault: boolean 832 updatedAt: string 833} 834 835export interface PdsTrustFactorsResponse { 836 factors: PdsTrustFactor[] 837 cursor: string | null 838} 839 840export interface TrustGraphStatus { 841 lastComputedAt: string | null 842 totalNodes: number 843 totalEdges: number 844 computationDurationMs: number 845 clustersFlagged: number 846 nextScheduledAt: string 847} 848 849export type BehavioralFlagType = 'burst_voting' | 'content_similarity' | 'low_diversity' 850export type BehavioralFlagStatus = 'pending' | 'dismissed' | 'action_taken' 851 852export interface BehavioralFlag { 853 id: number 854 flagType: BehavioralFlagType 855 affectedDids: string[] 856 details: string 857 detectedAt: string 858 status: BehavioralFlagStatus 859} 860 861export interface BehavioralFlagsResponse { 862 flags: BehavioralFlag[] 863} 864 865// --- Community Rules --- 866 867export interface CommunityRule { 868 id: number 869 title: string 870 description: string 871 displayOrder: number 872 createdAt: string 873 updatedAt: string 874 archivedAt: string | null 875} 876 877export interface CommunityRulesResponse { 878 data: CommunityRule[] 879} 880 881export interface CommunityRuleVersion { 882 id: number 883 ruleId: number 884 title: string 885 description: string 886 createdAt: string 887} 888 889export interface CommunityRuleVersionsResponse { 890 data: CommunityRuleVersion[] 891 cursor: string | null 892} 893 894export interface CreateRuleInput { 895 title: string 896 description: string 897} 898 899export interface UpdateRuleInput { 900 title: string 901 description: string 902} 903 904export interface ReorderRulesInput { 905 order: Array<{ id: number; displayOrder: number }> 906} 907 908// --- Shared --- 909 910export type MaturityRating = 'safe' | 'mature' | 'adult' 911 912// --- Pagination --- 913 914export interface PaginationParams { 915 limit?: number 916 cursor?: string 917}