Barazo default frontend
barazo.forum
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}