@@ -1063,6 +1063,23 @@ function stripUndefinedDeep(value: unknown): unknown {
10631063 return value ;
10641064}
10651065
1066+ function isSystemAuditActor ( actorUid : string , authType ?: string ) {
1067+ return (
1068+ authType === "service_account" ||
1069+ authType === "system" ||
1070+ authType === "api_key" ||
1071+ / g s e r v i c e a c c o u n t \. c o m $ / i. test ( actorUid )
1072+ ) ;
1073+ }
1074+
1075+ function pickAuditName ( value : unknown ) {
1076+ return typeof value === "string" ? value . trim ( ) : "" ;
1077+ }
1078+
1079+ function pickAuditHandle ( value : unknown ) {
1080+ return typeof value === "string" ? value . trim ( ) . replace ( / ^ @ / , "" ) : "" ;
1081+ }
1082+
10661083async function writeServerAuditLog ( params : {
10671084 actorUid : string ;
10681085 action : string ;
@@ -1072,42 +1089,78 @@ async function writeServerAuditLog(params: {
10721089 targetTitle ?: string ;
10731090 metadata ?: Record < string , unknown > ;
10741091 idempotencyKey ?: string ;
1092+ authType ?: string ;
1093+ actorHint ?: {
1094+ name ?: string ;
1095+ handle ?: string ;
1096+ role ?: string ;
1097+ email ?: string ;
1098+ photoURL ?: string ;
1099+ } ;
10751100} ) {
10761101 const firestore = getDb ( ) ;
1077- let actorName = "Staff" ;
1102+ const hint = params . actorHint || { } ;
1103+ let actorName = "" ;
10781104 let actorHandle = "" ;
1079- let actorRole = "staff " ;
1105+ let actorRole = "" ;
10801106 let actorEmail = "" ;
10811107 let actorPhotoURL = "" ;
10821108
1083- try {
1084- const [ authorDoc , authUser ] = await Promise . all ( [
1085- firestore . collection ( "authors" ) . doc ( params . actorUid ) . get ( ) ,
1086- admin . auth ( ) . getUser ( params . actorUid ) . catch ( ( ) => null ) ,
1087- ] ) ;
1088- if ( authorDoc . exists ) {
1089- const authorData = authorDoc . data ( ) ;
1090- actorName = authorData ?. name || authUser ?. displayName || actorName ;
1091- actorHandle = authorData ?. handle || "" ;
1092- actorRole = authorData ?. role || "staff" ;
1093- actorPhotoURL = authorData ?. avatar || authUser ?. photoURL || "" ;
1094- }
1095- actorEmail = authUser ?. email || "" ;
1096- if ( ! actorHandle ) {
1097- const userDoc = await firestore . collection ( "users" ) . doc ( params . actorUid ) . get ( ) ;
1098- if ( userDoc . exists ) {
1099- actorHandle = userDoc . data ( ) ?. handle || "" ;
1100- if ( ! actorPhotoURL ) actorPhotoURL = userDoc . data ( ) ?. photoURL || "" ;
1101- }
1109+ if ( isSystemAuditActor ( params . actorUid , params . authType ) ) {
1110+ actorName = "System" ;
1111+ actorRole = "system" ;
1112+ actorEmail = params . actorUid . includes ( "@" ) ? params . actorUid : "" ;
1113+ } else {
1114+ try {
1115+ const [ authorDoc , userDoc , authUser ] = await Promise . all ( [
1116+ firestore . collection ( "authors" ) . doc ( params . actorUid ) . get ( ) ,
1117+ firestore . collection ( "users" ) . doc ( params . actorUid ) . get ( ) ,
1118+ admin . auth ( ) . getUser ( params . actorUid ) . catch ( ( ) => null ) ,
1119+ ] ) ;
1120+ const authorData = authorDoc . exists ? authorDoc . data ( ) : undefined ;
1121+ const userData = userDoc . exists ? userDoc . data ( ) : undefined ;
1122+ actorName =
1123+ pickAuditName ( authorData ?. name ) ||
1124+ pickAuditName ( userData ?. staffName ) ||
1125+ pickAuditName ( userData ?. displayName ) ||
1126+ pickAuditName ( authUser ?. displayName ) ||
1127+ pickAuditName ( hint . name ) ||
1128+ pickAuditName ( authUser ?. email ?. split ( "@" ) [ 0 ] ) ||
1129+ params . actorUid ;
1130+ actorHandle =
1131+ pickAuditHandle ( authorData ?. handle ) ||
1132+ pickAuditHandle ( userData ?. handle ) ||
1133+ pickAuditHandle ( hint . handle ) ||
1134+ pickAuditHandle ( authUser ?. email ?. split ( "@" ) [ 0 ] ) ||
1135+ "" ;
1136+ actorRole =
1137+ pickAuditHandle ( authorData ?. role ) ||
1138+ pickAuditHandle ( hint . role ) ||
1139+ ( authorDoc . exists ? "staff" : "user" ) ;
1140+ actorEmail =
1141+ authUser ?. email ||
1142+ pickAuditName ( userData ?. email ) ||
1143+ pickAuditName ( hint . email ) ||
1144+ "" ;
1145+ actorPhotoURL =
1146+ pickAuditName ( authorData ?. avatar ) ||
1147+ pickAuditName ( userData ?. photoURL ) ||
1148+ authUser ?. photoURL ||
1149+ pickAuditName ( hint . photoURL ) ||
1150+ "" ;
1151+ } catch ( e ) {
1152+ logger . warn ( "Could not enrich server audit log actor details:" , e ) ;
1153+ actorName = pickAuditName ( hint . name ) || params . actorUid ;
1154+ actorHandle = pickAuditHandle ( hint . handle ) || "" ;
1155+ actorRole = pickAuditHandle ( hint . role ) || "user" ;
1156+ actorEmail = pickAuditName ( hint . email ) || "" ;
11021157 }
1103- } catch ( e ) {
1104- logger . warn ( "Could not enrich server audit log actor details:" , e ) ;
11051158 }
11061159
11071160 const auditData = stripUndefinedDeep ( {
11081161 actorUid : params . actorUid ,
11091162 actorName,
1110- actorHandle : actorHandle || actorEmail . split ( "@" ) [ 0 ] || "staff" ,
1163+ actorHandle,
11111164 actorEmail,
11121165 actorRole,
11131166 actorPhotoURL,
@@ -1142,6 +1195,7 @@ function changedTopLevelKeys(
11421195async function auditAuthenticatedClientWrite ( params : {
11431196 eventId : string ;
11441197 actorUid ?: string ;
1198+ authType ?: string ;
11451199 collection : string ;
11461200 documentId : string ;
11471201 category : "articles" | "comments" | "team" | "profile" ;
@@ -1150,8 +1204,21 @@ async function auditAuthenticatedClientWrite(params: {
11501204} ) {
11511205 if ( ! params . actorUid ) return ;
11521206 const operation = ! params . before ? "create" : ! params . after ? "delete" : "update" ;
1207+ const snapshot = params . after || params . before ;
1208+ const actorHint =
1209+ params . documentId === params . actorUid
1210+ ? {
1211+ name : pickAuditName ( snapshot ?. name ) || pickAuditName ( snapshot ?. displayName ) || pickAuditName ( snapshot ?. staffName ) ,
1212+ handle : pickAuditHandle ( snapshot ?. handle ) ,
1213+ role : pickAuditHandle ( snapshot ?. role ) ,
1214+ email : pickAuditName ( snapshot ?. email ) ,
1215+ photoURL : pickAuditName ( snapshot ?. avatar ) || pickAuditName ( snapshot ?. photoURL ) ,
1216+ }
1217+ : undefined ;
11531218 await writeServerAuditLog ( {
11541219 actorUid : params . actorUid ,
1220+ authType : params . authType ,
1221+ actorHint,
11551222 action : `${ params . collection } .${ operation } ` ,
11561223 category : params . category ,
11571224 details : `${ operation } ${ params . collection } record ${ params . documentId } ` ,
@@ -1172,6 +1239,7 @@ export const auditClientArticleWrite = onDocumentWrittenWithAuthContext(
11721239 async ( event ) => auditAuthenticatedClientWrite ( {
11731240 eventId : event . id ,
11741241 actorUid : event . authId ,
1242+ authType : event . authType ,
11751243 collection : "article" ,
11761244 documentId : event . params . documentId ,
11771245 category : "articles" ,
@@ -1185,6 +1253,7 @@ export const auditClientArticleTrashWrite = onDocumentWrittenWithAuthContext(
11851253 async ( event ) => auditAuthenticatedClientWrite ( {
11861254 eventId : event . id ,
11871255 actorUid : event . authId ,
1256+ authType : event . authType ,
11881257 collection : "article_trash" ,
11891258 documentId : event . params . documentId ,
11901259 category : "articles" ,
@@ -1198,6 +1267,7 @@ export const auditClientAuthorWrite = onDocumentWrittenWithAuthContext(
11981267 async ( event ) => auditAuthenticatedClientWrite ( {
11991268 eventId : event . id ,
12001269 actorUid : event . authId ,
1270+ authType : event . authType ,
12011271 collection : "author_profile" ,
12021272 documentId : event . params . documentId ,
12031273 category : "profile" ,
@@ -1211,6 +1281,7 @@ export const auditClientCommentWrite = onDocumentWrittenWithAuthContext(
12111281 async ( event ) => auditAuthenticatedClientWrite ( {
12121282 eventId : event . id ,
12131283 actorUid : event . authId ,
1284+ authType : event . authType ,
12141285 collection : "comment" ,
12151286 documentId : event . params . documentId ,
12161287 category : "comments" ,
@@ -1224,6 +1295,7 @@ export const auditClientReplyWrite = onDocumentWrittenWithAuthContext(
12241295 async ( event ) => auditAuthenticatedClientWrite ( {
12251296 eventId : event . id ,
12261297 actorUid : event . authId ,
1298+ authType : event . authType ,
12271299 collection : "comment_reply" ,
12281300 documentId : event . params . documentId ,
12291301 category : "comments" ,
@@ -1237,6 +1309,7 @@ export const auditClientReaderProfileWrite = onDocumentWrittenWithAuthContext(
12371309 async ( event ) => auditAuthenticatedClientWrite ( {
12381310 eventId : event . id ,
12391311 actorUid : event . authId ,
1312+ authType : event . authType ,
12401313 collection : "reader_profile" ,
12411314 documentId : event . params . documentId ,
12421315 category : "profile" ,
0 commit comments