@codingfactory/socialkit-vue 0.7.10 → 0.7.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/services/circles.d.ts +16 -4
- package/dist/services/circles.d.ts.map +1 -1
- package/dist/services/circles.js +161 -33
- package/dist/services/circles.js.map +1 -1
- package/dist/stores/circles.d.ts.map +1 -1
- package/dist/stores/circles.js +22 -4
- package/dist/stores/circles.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +2 -0
- package/src/services/circles.ts +215 -39
- package/src/stores/circles.ts +32 -5
package/src/services/circles.ts
CHANGED
|
@@ -287,6 +287,19 @@ export interface CircleModerationReport {
|
|
|
287
287
|
resolution_notes?: string | null
|
|
288
288
|
}
|
|
289
289
|
|
|
290
|
+
export interface CircleModerationReportSubmissionInput {
|
|
291
|
+
category: string
|
|
292
|
+
notes?: string | null
|
|
293
|
+
subject_user_id?: string | null
|
|
294
|
+
subject_type?: CircleModerationReportSubjectType
|
|
295
|
+
subject_id?: string | null
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
export interface CircleModerationReportSubmissionResult {
|
|
299
|
+
report_id: string
|
|
300
|
+
status: string
|
|
301
|
+
}
|
|
302
|
+
|
|
290
303
|
export interface CircleBanRecord {
|
|
291
304
|
id: string
|
|
292
305
|
user_id: string
|
|
@@ -880,6 +893,10 @@ function circleUserRole(circle: Circle | null | undefined): CircleRole | null {
|
|
|
880
893
|
return isCircleRole(circle.user_role) ? circle.user_role : null
|
|
881
894
|
}
|
|
882
895
|
|
|
896
|
+
function hasValue(value: unknown): boolean {
|
|
897
|
+
return value !== undefined && value !== null
|
|
898
|
+
}
|
|
899
|
+
|
|
883
900
|
function normalizeCollectionResponse<T>(
|
|
884
901
|
payload: unknown,
|
|
885
902
|
normalizeItem: (value: unknown) => T | null,
|
|
@@ -925,6 +942,12 @@ function normalizeInlineCollection<T>(
|
|
|
925
942
|
}
|
|
926
943
|
}
|
|
927
944
|
|
|
945
|
+
function normalizeBootstrapRoleCollection(value: unknown): PaginationResponse<CircleRoleDefinition> {
|
|
946
|
+
return Array.isArray(value)
|
|
947
|
+
? normalizeInlineCollection(value, normalizeRoleDefinition)
|
|
948
|
+
: normalizeCollectionResponse(value ?? {}, normalizeRoleDefinition)
|
|
949
|
+
}
|
|
950
|
+
|
|
928
951
|
function normalizeCircle(value: unknown): Circle | null {
|
|
929
952
|
if (!isRecord(value)) {
|
|
930
953
|
return null
|
|
@@ -1714,6 +1737,73 @@ class CirclesService {
|
|
|
1714
1737
|
return matchedCircle ? normalizeCircle(matchedCircle) : null
|
|
1715
1738
|
}
|
|
1716
1739
|
|
|
1740
|
+
private normalizeModerationReportSubmissionInput(
|
|
1741
|
+
subjectOrInput: string | CircleModerationReportSubmissionInput,
|
|
1742
|
+
category?: string,
|
|
1743
|
+
notes?: string,
|
|
1744
|
+
): CircleModerationReportSubmissionInput {
|
|
1745
|
+
if (typeof subjectOrInput !== 'string') {
|
|
1746
|
+
return subjectOrInput
|
|
1747
|
+
}
|
|
1748
|
+
|
|
1749
|
+
const input: CircleModerationReportSubmissionInput = {
|
|
1750
|
+
category: category ?? '',
|
|
1751
|
+
subject_user_id: subjectOrInput,
|
|
1752
|
+
subject_type: 'user',
|
|
1753
|
+
subject_id: subjectOrInput,
|
|
1754
|
+
}
|
|
1755
|
+
|
|
1756
|
+
if (typeof notes === 'string') {
|
|
1757
|
+
input.notes = notes
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1760
|
+
return input
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1763
|
+
private buildModerationReportPayload(input: CircleModerationReportSubmissionInput): Record<string, string> {
|
|
1764
|
+
const category = this.sanitizeInput(input.category)
|
|
1765
|
+
if (category.length === 0) {
|
|
1766
|
+
throw new Error('Moderation report category is required.')
|
|
1767
|
+
}
|
|
1768
|
+
|
|
1769
|
+
const subjectUserId = typeof input.subject_user_id === 'string' && input.subject_user_id.trim().length > 0
|
|
1770
|
+
? this.sanitizeInput(input.subject_user_id)
|
|
1771
|
+
: null
|
|
1772
|
+
const subjectType = input.subject_type ?? (subjectUserId !== null ? 'user' : undefined)
|
|
1773
|
+
const derivedSubjectId = typeof input.subject_id === 'string' && input.subject_id.trim().length > 0
|
|
1774
|
+
? this.sanitizeInput(input.subject_id)
|
|
1775
|
+
: subjectType === 'user'
|
|
1776
|
+
? subjectUserId
|
|
1777
|
+
: null
|
|
1778
|
+
|
|
1779
|
+
if (subjectType !== undefined && derivedSubjectId === null) {
|
|
1780
|
+
throw new Error('Moderation report subject_id is required when subject_type is provided.')
|
|
1781
|
+
}
|
|
1782
|
+
|
|
1783
|
+
if (subjectType === undefined && subjectUserId === null) {
|
|
1784
|
+
throw new Error('Moderation report subject details are required.')
|
|
1785
|
+
}
|
|
1786
|
+
|
|
1787
|
+
const payload: Record<string, string> = {
|
|
1788
|
+
category,
|
|
1789
|
+
}
|
|
1790
|
+
|
|
1791
|
+
if (subjectUserId !== null) {
|
|
1792
|
+
payload.subject_user_id = subjectUserId
|
|
1793
|
+
}
|
|
1794
|
+
|
|
1795
|
+
if (subjectType !== undefined && derivedSubjectId !== null) {
|
|
1796
|
+
payload.subject_type = subjectType
|
|
1797
|
+
payload.subject_id = derivedSubjectId
|
|
1798
|
+
}
|
|
1799
|
+
|
|
1800
|
+
if (typeof input.notes === 'string' && input.notes.trim().length > 0) {
|
|
1801
|
+
payload.notes = this.sanitizeInput(input.notes)
|
|
1802
|
+
}
|
|
1803
|
+
|
|
1804
|
+
return payload
|
|
1805
|
+
}
|
|
1806
|
+
|
|
1717
1807
|
public async list(cursor?: string | null, filters?: CircleFilters): Promise<PaginationResponse<Circle>> {
|
|
1718
1808
|
try {
|
|
1719
1809
|
const params: Record<string, string> = {
|
|
@@ -2265,21 +2355,12 @@ class CirclesService {
|
|
|
2265
2355
|
}
|
|
2266
2356
|
}
|
|
2267
2357
|
|
|
2268
|
-
public async
|
|
2358
|
+
public async submitModerationReport(
|
|
2269
2359
|
circleId: string,
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
notes?: string,
|
|
2273
|
-
): Promise<{ report_id: string; status: string }> {
|
|
2360
|
+
input: CircleModerationReportSubmissionInput,
|
|
2361
|
+
): Promise<CircleModerationReportSubmissionResult> {
|
|
2274
2362
|
try {
|
|
2275
|
-
const payload
|
|
2276
|
-
subject_user_id: subjectUserId,
|
|
2277
|
-
category: this.sanitizeInput(category),
|
|
2278
|
-
}
|
|
2279
|
-
if (typeof notes === 'string' && notes.trim().length > 0) {
|
|
2280
|
-
payload.notes = this.sanitizeInput(notes)
|
|
2281
|
-
}
|
|
2282
|
-
|
|
2363
|
+
const payload = this.buildModerationReportPayload(input)
|
|
2283
2364
|
const response = await this.client.post(`${this.baseURL}/${circleId}/moderation/reports`, payload)
|
|
2284
2365
|
const data = readRecord(response.data, 'data') ?? {}
|
|
2285
2366
|
const reportId = readString(data.report_id)
|
|
@@ -2303,6 +2384,28 @@ class CirclesService {
|
|
|
2303
2384
|
}
|
|
2304
2385
|
}
|
|
2305
2386
|
|
|
2387
|
+
public async reportMember(
|
|
2388
|
+
circleId: string,
|
|
2389
|
+
subjectUserId: string,
|
|
2390
|
+
category: string,
|
|
2391
|
+
notes?: string,
|
|
2392
|
+
): Promise<CircleModerationReportSubmissionResult>
|
|
2393
|
+
public async reportMember(
|
|
2394
|
+
circleId: string,
|
|
2395
|
+
input: CircleModerationReportSubmissionInput,
|
|
2396
|
+
): Promise<CircleModerationReportSubmissionResult>
|
|
2397
|
+
public async reportMember(
|
|
2398
|
+
circleId: string,
|
|
2399
|
+
subjectOrInput: string | CircleModerationReportSubmissionInput,
|
|
2400
|
+
category?: string,
|
|
2401
|
+
notes?: string,
|
|
2402
|
+
): Promise<CircleModerationReportSubmissionResult> {
|
|
2403
|
+
return await this.submitModerationReport(
|
|
2404
|
+
circleId,
|
|
2405
|
+
this.normalizeModerationReportSubmissionInput(subjectOrInput, category, notes),
|
|
2406
|
+
)
|
|
2407
|
+
}
|
|
2408
|
+
|
|
2306
2409
|
public async listModerationReports(
|
|
2307
2410
|
circleId: string,
|
|
2308
2411
|
cursor?: string | null,
|
|
@@ -2508,6 +2611,10 @@ class CirclesService {
|
|
|
2508
2611
|
public async getManagementBootstrap(circleId: string): Promise<CircleManagementBootstrap> {
|
|
2509
2612
|
const identifier = this.sanitizeInput(circleId)
|
|
2510
2613
|
let resolvedCircle: Circle | null = null
|
|
2614
|
+
let bootstrapPermissionCatalog = createEmptyPagination<CirclePermissionCatalogEntry>().data
|
|
2615
|
+
let bootstrapManagementSections: CircleManagementSection[] = []
|
|
2616
|
+
let bootstrapCounts = createEmptyManagementCounts()
|
|
2617
|
+
let hasBootstrapCounts = false
|
|
2511
2618
|
const bootstrapIdentifier = this.isUuid(identifier)
|
|
2512
2619
|
? identifier
|
|
2513
2620
|
: await (async (): Promise<string> => {
|
|
@@ -2520,7 +2627,14 @@ class CirclesService {
|
|
|
2520
2627
|
const data = readRecord(response.data, 'data')
|
|
2521
2628
|
const bootstrapCircle = data?.circle
|
|
2522
2629
|
const bootstrapActorCapabilities = readRecord(data, 'actor_capabilities')
|
|
2523
|
-
|
|
2630
|
+
bootstrapManagementSections = normalizeManagementSections(data?.management_sections)
|
|
2631
|
+
bootstrapPermissionCatalog = Array.isArray(data?.permission_catalog)
|
|
2632
|
+
? data.permission_catalog
|
|
2633
|
+
.map((entry) => normalizePermissionCatalogEntry(entry))
|
|
2634
|
+
.filter((entry): entry is CirclePermissionCatalogEntry => entry !== null)
|
|
2635
|
+
: []
|
|
2636
|
+
hasBootstrapCounts = hasValue(data?.counts)
|
|
2637
|
+
bootstrapCounts = normalizeManagementCounts(data?.counts)
|
|
2524
2638
|
const directCircle = normalizeCircle(bootstrapCircle ?? data)
|
|
2525
2639
|
const directCircleRole = circleUserRole(directCircle)
|
|
2526
2640
|
const fallbackRole = directCircleRole ?? circleUserRole(resolvedCircle)
|
|
@@ -2546,38 +2660,100 @@ class CirclesService {
|
|
|
2546
2660
|
const circle = normalizeCircle(mergedCircleSource)
|
|
2547
2661
|
|
|
2548
2662
|
if (circle !== null) {
|
|
2549
|
-
const
|
|
2550
|
-
|
|
2551
|
-
if (bootstrapHasCollections) {
|
|
2552
|
-
const actor = circle.actor ?? buildActorContext(circle)
|
|
2663
|
+
const actor = circle.actor ?? buildActorContext(circle)
|
|
2664
|
+
if (!actor.capabilities.canViewManagement) {
|
|
2553
2665
|
return {
|
|
2554
2666
|
circle,
|
|
2555
2667
|
actor,
|
|
2556
2668
|
management_sections: bootstrapManagementSections.length > 0
|
|
2557
2669
|
? bootstrapManagementSections
|
|
2558
2670
|
: actor.managementSections,
|
|
2559
|
-
members:
|
|
2560
|
-
requests:
|
|
2561
|
-
roles:
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
: [],
|
|
2570
|
-
reports: normalizeCollectionResponse(data?.reports ?? {}, normalizeModerationReport),
|
|
2571
|
-
bans: normalizeCollectionResponse(data?.bans ?? {}, normalizeBanRecord),
|
|
2572
|
-
mutes: normalizeCollectionResponse(data?.mutes ?? {}, normalizeMuteRecord),
|
|
2573
|
-
audit: normalizeCollectionResponse(data?.audit ?? {}, normalizeAuditLogEntry),
|
|
2574
|
-
automod: normalizeCollectionResponse(data?.automod ?? {}, normalizeAutomodRule),
|
|
2671
|
+
members: createEmptyPagination<CircleMember>(),
|
|
2672
|
+
requests: createEmptyPagination<JoinRequest>(),
|
|
2673
|
+
roles: createEmptyPagination<CircleRoleDefinition>(),
|
|
2674
|
+
counts: createEmptyManagementCounts(),
|
|
2675
|
+
permission_catalog: [],
|
|
2676
|
+
reports: createEmptyPagination<CircleModerationReport>(),
|
|
2677
|
+
bans: createEmptyPagination<CircleBanRecord>(),
|
|
2678
|
+
mutes: createEmptyPagination<CircleMuteRecord>(),
|
|
2679
|
+
audit: createEmptyPagination<CircleModerationAuditLogEntry>(),
|
|
2680
|
+
automod: createEmptyPagination<CircleAutomodRule>(),
|
|
2575
2681
|
}
|
|
2576
2682
|
}
|
|
2577
2683
|
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2684
|
+
const caps = actor.capabilities
|
|
2685
|
+
const emptyJoinRequests = createEmptyPagination<JoinRequest>()
|
|
2686
|
+
const emptyRoles = createEmptyPagination<CircleRoleDefinition>()
|
|
2687
|
+
const emptyReports = createEmptyPagination<CircleModerationReport>()
|
|
2688
|
+
const emptyBans = createEmptyPagination<CircleBanRecord>()
|
|
2689
|
+
const emptyMutes = createEmptyPagination<CircleMuteRecord>()
|
|
2690
|
+
const emptyAudit = createEmptyPagination<CircleModerationAuditLogEntry>()
|
|
2691
|
+
const emptyAutomod = createEmptyPagination<CircleAutomodRule>()
|
|
2692
|
+
const members = hasValue(data?.members)
|
|
2693
|
+
? normalizeCollectionResponse(data?.members ?? {}, normalizeCircleMember)
|
|
2694
|
+
: await this.getMembers(circle.id)
|
|
2695
|
+
const requests = hasValue(data?.requests)
|
|
2696
|
+
? normalizeCollectionResponse(data?.requests ?? {}, normalizeJoinRequest)
|
|
2697
|
+
: caps.canReviewRequests
|
|
2698
|
+
? await this.listJoinRequests(circle.id, null, 'pending')
|
|
2699
|
+
: emptyJoinRequests
|
|
2700
|
+
const roles = hasValue(data?.roles)
|
|
2701
|
+
? normalizeBootstrapRoleCollection(data?.roles)
|
|
2702
|
+
: caps.canManageRoles
|
|
2703
|
+
? await this.listRoles(circle.id)
|
|
2704
|
+
: emptyRoles
|
|
2705
|
+
const reports = hasValue(data?.reports)
|
|
2706
|
+
? normalizeCollectionResponse(data?.reports ?? {}, normalizeModerationReport)
|
|
2707
|
+
: caps.canManageReports
|
|
2708
|
+
? await this.listModerationReports(circle.id, null, 'pending')
|
|
2709
|
+
: emptyReports
|
|
2710
|
+
const bans = hasValue(data?.bans)
|
|
2711
|
+
? normalizeCollectionResponse(data?.bans ?? {}, normalizeBanRecord)
|
|
2712
|
+
: caps.canManageBans
|
|
2713
|
+
? await this.listBans(circle.id)
|
|
2714
|
+
: emptyBans
|
|
2715
|
+
const mutes = hasValue(data?.mutes)
|
|
2716
|
+
? normalizeCollectionResponse(data?.mutes ?? {}, normalizeMuteRecord)
|
|
2717
|
+
: caps.canManageMutes
|
|
2718
|
+
? await this.listMutes(circle.id)
|
|
2719
|
+
: emptyMutes
|
|
2720
|
+
const audit = hasValue(data?.audit)
|
|
2721
|
+
? normalizeCollectionResponse(data?.audit ?? {}, normalizeAuditLogEntry)
|
|
2722
|
+
: caps.canViewAuditLog
|
|
2723
|
+
? await this.getModerationAuditLog(circle.id)
|
|
2724
|
+
: emptyAudit
|
|
2725
|
+
const automod = hasValue(data?.automod)
|
|
2726
|
+
? normalizeCollectionResponse(data?.automod ?? {}, normalizeAutomodRule)
|
|
2727
|
+
: caps.canManageAutomod
|
|
2728
|
+
? await this.listAutomodRules(circle.id)
|
|
2729
|
+
: emptyAutomod
|
|
2730
|
+
|
|
2731
|
+
return {
|
|
2732
|
+
circle,
|
|
2733
|
+
actor,
|
|
2734
|
+
management_sections: bootstrapManagementSections.length > 0
|
|
2735
|
+
? bootstrapManagementSections
|
|
2736
|
+
: actor.managementSections,
|
|
2737
|
+
members,
|
|
2738
|
+
requests,
|
|
2739
|
+
roles,
|
|
2740
|
+
counts: hasBootstrapCounts
|
|
2741
|
+
? bootstrapCounts
|
|
2742
|
+
: {
|
|
2743
|
+
members: circle.member_count ?? members.data.length,
|
|
2744
|
+
requests_pending: requests.data.length,
|
|
2745
|
+
reports_pending: reports.data.length,
|
|
2746
|
+
roles: roles.data.length,
|
|
2747
|
+
bans_active: bans.data.length,
|
|
2748
|
+
mutes_active: mutes.data.length,
|
|
2749
|
+
},
|
|
2750
|
+
permission_catalog: bootstrapPermissionCatalog,
|
|
2751
|
+
reports,
|
|
2752
|
+
bans,
|
|
2753
|
+
mutes,
|
|
2754
|
+
audit,
|
|
2755
|
+
automod,
|
|
2756
|
+
}
|
|
2581
2757
|
}
|
|
2582
2758
|
} catch (error: unknown) {
|
|
2583
2759
|
if (!this.isHttpStatus(error, 404)) {
|
|
@@ -2597,7 +2773,7 @@ class CirclesService {
|
|
|
2597
2773
|
requests: createEmptyPagination<JoinRequest>(),
|
|
2598
2774
|
roles: createEmptyPagination<CircleRoleDefinition>(),
|
|
2599
2775
|
counts: createEmptyManagementCounts(),
|
|
2600
|
-
permission_catalog:
|
|
2776
|
+
permission_catalog: bootstrapPermissionCatalog,
|
|
2601
2777
|
reports: createEmptyPagination<CircleModerationReport>(),
|
|
2602
2778
|
bans: createEmptyPagination<CircleBanRecord>(),
|
|
2603
2779
|
mutes: createEmptyPagination<CircleMuteRecord>(),
|
|
@@ -2664,7 +2840,7 @@ class CirclesService {
|
|
|
2664
2840
|
bans_active: bans.data.length,
|
|
2665
2841
|
mutes_active: mutes.data.length,
|
|
2666
2842
|
},
|
|
2667
|
-
permission_catalog:
|
|
2843
|
+
permission_catalog: bootstrapPermissionCatalog,
|
|
2668
2844
|
reports,
|
|
2669
2845
|
bans,
|
|
2670
2846
|
mutes,
|
package/src/stores/circles.ts
CHANGED
|
@@ -460,10 +460,27 @@ const deriveRoleAssignmentsFromMembers = (members: CircleMemberState[]): CircleR
|
|
|
460
460
|
roleAssignment.user = member.user
|
|
461
461
|
}
|
|
462
462
|
|
|
463
|
-
|
|
463
|
+
return roleAssignment
|
|
464
464
|
})
|
|
465
465
|
}
|
|
466
466
|
|
|
467
|
+
const resolveCircleActorContext = (
|
|
468
|
+
state: Pick<CirclesState, 'managementActors' | 'currentCircle' | 'circles'>,
|
|
469
|
+
circleId: string,
|
|
470
|
+
): CircleActorContext | null => {
|
|
471
|
+
const managementActor = state.managementActors[circleId]
|
|
472
|
+
if (managementActor) {
|
|
473
|
+
return managementActor
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
if (state.currentCircle?.id === circleId || state.currentCircle?.slug === circleId) {
|
|
477
|
+
return state.currentCircle.actor ?? null
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
const matchingCircle = state.circles.find((candidate) => candidate.id === circleId || candidate.slug === circleId)
|
|
481
|
+
return matchingCircle?.actor ?? null
|
|
482
|
+
}
|
|
483
|
+
|
|
467
484
|
const getRoleStoreService = (circlesService: CirclesServiceInstance): Partial<CircleRoleStoreService> => {
|
|
468
485
|
// The shared service layer is mid-migration; runtime method checks let the store
|
|
469
486
|
// support both the legacy member-role API and the new RBAC role-definition API.
|
|
@@ -1669,10 +1686,20 @@ export function createCirclesStoreDefinition(config: CirclesStoreConfig) {
|
|
|
1669
1686
|
|
|
1670
1687
|
try {
|
|
1671
1688
|
await circlesService.reportMember(circleId, subjectUserId, category, notes)
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1689
|
+
const actor = resolveCircleActorContext(this, circleId)
|
|
1690
|
+
const followUpTasks: Promise<unknown>[] = []
|
|
1691
|
+
|
|
1692
|
+
if (actor?.capabilities.canManageReports === true) {
|
|
1693
|
+
followUpTasks.push(this.fetchModerationReports(circleId, true))
|
|
1694
|
+
}
|
|
1695
|
+
|
|
1696
|
+
if (actor?.capabilities.canViewAuditLog === true) {
|
|
1697
|
+
followUpTasks.push(this.fetchAuditLog(circleId, true))
|
|
1698
|
+
}
|
|
1699
|
+
|
|
1700
|
+
if (followUpTasks.length > 0) {
|
|
1701
|
+
await Promise.allSettled(followUpTasks)
|
|
1702
|
+
}
|
|
1676
1703
|
} catch (error: unknown) {
|
|
1677
1704
|
this.error = getErrorMessage(error)
|
|
1678
1705
|
throw error
|