feat: implement audit logging system with service, controller, and interceptor to track user actions across core services

This commit is contained in:
pradeepkumar
2026-04-28 11:12:25 +05:30
parent 4eda556059
commit 80c6fd1d71
27 changed files with 895 additions and 29 deletions

View File

@@ -14,6 +14,8 @@ import { StripeService } from './stripe.service';
import { SubscriptionService } from './subscription.service';
import { Public } from '../auth/decorators/public.decorator';
import { SubscriptionStatus } from '@prisma/client';
import { AuditService } from '../audit/audit.service';
import { AuditAction } from '../audit/audit.constants';
@ApiTags('Stripe Webhooks')
@Controller('stripe')
@@ -23,6 +25,7 @@ export class StripeWebhookController {
constructor(
private stripeService: StripeService,
private subscriptionService: SubscriptionService,
private audit: AuditService,
) {}
@Public()
@@ -131,6 +134,14 @@ export class StripeWebhookController {
currentPeriodEnd,
});
this.audit.log({
action: AuditAction.SUBSCRIPTION_CREATE,
resourceType: 'AgentSubscription',
resourceId: stripeSubscriptionId,
metadata: { userId, planId, stripeCustomerId },
actorOverride: { id: null, role: 'SYSTEM' },
});
this.logger.log(`Created subscription for user ${userId}, plan ${planId}`);
}
@@ -156,6 +167,19 @@ export class StripeWebhookController {
receiptUrl: invoice.hosted_invoice_url,
});
this.audit.log({
action: AuditAction.PAYMENT_SUCCESS,
resourceType: 'Payment',
resourceId: invoice.id,
metadata: {
userId: subscription.userId,
amount: invoice.amount_paid,
currency: invoice.currency,
stripeSubscriptionId,
},
actorOverride: { id: null, role: 'SYSTEM' },
});
// Update subscription period
const periodEnd = invoice.lines?.data?.[0]?.period?.end;
const periodStart = invoice.lines?.data?.[0]?.period?.start;
@@ -193,6 +217,19 @@ export class StripeWebhookController {
SubscriptionStatus.PAST_DUE,
);
this.audit.log({
action: AuditAction.PAYMENT_FAILURE,
resourceType: 'Payment',
resourceId: invoice.id,
metadata: {
userId: subscription.userId,
amount: invoice.amount_due,
currency: invoice.currency,
stripeSubscriptionId,
},
actorOverride: { id: null, role: 'SYSTEM' },
});
this.logger.warn(`Payment failed for subscription ${stripeSubscriptionId}`);
}
@@ -224,6 +261,14 @@ export class StripeWebhookController {
{ canceledAt: new Date() },
);
this.audit.log({
action: AuditAction.SUBSCRIPTION_CANCEL,
resourceType: 'AgentSubscription',
resourceId: subscription.id,
metadata: { source: 'stripe_webhook' },
actorOverride: { id: null, role: 'SYSTEM' },
});
this.logger.log(`Subscription ${subscription.id} cancelled`);
}
}

View File

@@ -2,6 +2,8 @@ import { Injectable, Logger, BadRequestException } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import Stripe from 'stripe';
import { PrismaService } from '../prisma/prisma.service';
import { AuditService } from '../audit/audit.service';
import { AuditAction } from '../audit/audit.constants';
@Injectable()
export class StripeService {
@@ -11,6 +13,7 @@ export class StripeService {
constructor(
private configService: ConfigService,
private prisma: PrismaService,
private audit: AuditService,
) {
const secretKey = this.configService.get<string>('stripe.secretKey');
if (secretKey) {
@@ -180,13 +183,20 @@ export class StripeService {
stripeSubscriptionId: string,
atPeriodEnd = true,
): Promise<Stripe.Subscription> {
if (atPeriodEnd) {
return this.getStripe().subscriptions.update(stripeSubscriptionId, {
cancel_at_period_end: true,
});
}
const result = atPeriodEnd
? await this.getStripe().subscriptions.update(stripeSubscriptionId, {
cancel_at_period_end: true,
})
: await this.getStripe().subscriptions.cancel(stripeSubscriptionId);
return this.getStripe().subscriptions.cancel(stripeSubscriptionId);
this.audit.log({
action: AuditAction.SUBSCRIPTION_CANCEL,
resourceType: 'AgentSubscription',
resourceId: stripeSubscriptionId,
metadata: { atPeriodEnd, source: 'agent_action' },
});
return result;
}
async getSubscription(