11import { AuditAction , AuditResourceType } from '@sim/audit'
22import { db } from '@sim/db'
3+ import { createLogger } from '@sim/logger'
34import { isOrgAdminRole } from '@sim/platform-authz/workspace'
4- import { getPostgresErrorCode } from '@sim/utils/errors'
5+ import { getPostgresErrorCode , toError } from '@sim/utils/errors'
56import {
67 defineAuthorizedOrganizationUseCase ,
78 type OrganizationUseCaseContext ,
@@ -17,6 +18,8 @@ import type { SimLinkAccess, SimLinkRecord, SimLinkViewer } from '@/lib/sim-link
1718import * as repository from '@/lib/sim-links/repository'
1819import type { SimLinkFields , SimLinkSort } from '@/lib/sim-links/validation'
1920
21+ const logger = createLogger ( 'SimLinks' )
22+
2023interface OrganizationInput {
2124 organizationId : string
2225}
@@ -94,20 +97,24 @@ async function withUniqueName<T>(
9497function auditOf (
9598 action : WorkspaceUseCaseAuditEntry [ 'action' ] ,
9699 verb : string ,
97- link : Pick < SimLinkRecord , 'id' | 'name' > & Partial < SimLinkRecord >
100+ link : { id : string ; name : string } ,
101+ metadata ?: Record < string , unknown >
98102) : WorkspaceUseCaseAuditEntry {
99103 return {
100104 action,
101105 resourceType : AuditResourceType . SIM_LINK ,
102106 resourceId : link . id ,
103107 resourceName : `sim/${ link . name } ` ,
104108 description : `${ verb } sim/${ link . name } ` ,
105- ...( link . url !== undefined && {
106- metadata : { url : link . url , unlisted : link . unlisted , restrictEdit : link . restrictEdit } ,
107- } ) ,
109+ metadata,
108110 }
109111}
110112
113+ /** What a saved link now does, for its audit entry. */
114+ function savedMetadata ( link : SimLinkView ) : Record < string , unknown > {
115+ return { url : link . url , unlisted : link . unlisted , restrictEdit : link . restrictEdit }
116+ }
117+
111118/** Read in the writing transaction, so the result and its audit describe exactly this write. */
112119async function readView (
113120 tx : DbTransaction ,
@@ -150,7 +157,8 @@ export const createSimLink = defineAuthorizedOrganizationUseCase({
150157 } )
151158 )
152159 } ,
153- projectAudit : ( { result } ) => auditOf ( AuditAction . SIM_LINK_CREATED , 'Created' , result ) ,
160+ projectAudit : ( { result } ) =>
161+ auditOf ( AuditAction . SIM_LINK_CREATED , 'Created' , result , savedMetadata ( result ) ) ,
154162} )
155163
156164export const updateSimLink = defineAuthorizedOrganizationUseCase ( {
@@ -176,7 +184,8 @@ export const updateSimLink = defineAuthorizedOrganizationUseCase({
176184 } )
177185 )
178186 } ,
179- projectAudit : ( { result } ) => auditOf ( AuditAction . SIM_LINK_UPDATED , 'Updated' , result ) ,
187+ projectAudit : ( { result } ) =>
188+ auditOf ( AuditAction . SIM_LINK_UPDATED , 'Updated' , result , savedMetadata ( result ) ) ,
180189} )
181190
182191export const deleteSimLink = defineAuthorizedOrganizationUseCase ( {
@@ -192,6 +201,19 @@ export const deleteSimLink = defineAuthorizedOrganizationUseCase({
192201 projectAudit : ( { result } ) => auditOf ( AuditAction . SIM_LINK_DELETED , 'Deleted' , result ) ,
193202} )
194203
204+ /** Visit counts rank links; failing to record one must never block the redirect it describes. */
205+ async function countVisit ( organizationId : string , linkId : string ) : Promise < void > {
206+ try {
207+ await repository . recordSimLinkVisit ( organizationId , linkId )
208+ } catch ( error ) {
209+ logger . warn ( 'Could not count a Sim link visit' , {
210+ organizationId,
211+ linkId,
212+ error : toError ( error ) ,
213+ } )
214+ }
215+ }
216+
195217/**
196218 * Unlisted links resolve for every member: unlisting hides a link from browsing, not from use.
197219 * Only a redirect counts as a visit; a link opened without the values it needs does not.
@@ -207,8 +229,7 @@ export const resolveSimLink = defineAuthorizedOrganizationUseCase({
207229 const link = await repository . findSimLinkDestination ( input . organizationId , input . name )
208230 if ( ! link ) return { kind : 'not_found' }
209231 const target = resolveSimLinkTarget ( link , input . values )
210- if ( target . kind === 'redirect' )
211- await repository . recordSimLinkVisit ( input . organizationId , link . id )
232+ if ( target . kind === 'redirect' ) await countVisit ( input . organizationId , link . id )
212233 return target
213234 } ,
214235} )
0 commit comments