Browse Source

feat(api): implement access logging

pull/8/head
isra el 2 years ago
parent
commit
d10fa350d9
  1. 5
      api/src/auth/auth.module.ts
  2. 39
      api/src/auth/auth.service.ts
  3. 3
      api/src/auth/guards/auth.guard.ts
  4. 31
      api/src/auth/schemas/access-log.schema.ts

5
api/src/auth/auth.module.ts

@ -12,6 +12,7 @@ import {
PasswordReset, PasswordReset,
PasswordResetSchema, PasswordResetSchema,
} from './schemas/password-reset.schema' } from './schemas/password-reset.schema'
import { AccessLog, AccessLogSchema } from './schemas/access-log.schema'
@Module({ @Module({
imports: [ imports: [
@ -24,6 +25,10 @@ import {
name: PasswordReset.name, name: PasswordReset.name,
schema: PasswordResetSchema, schema: PasswordResetSchema,
}, },
{
name: AccessLog.name,
schema: AccessLogSchema,
},
]), ]),
UsersModule, UsersModule,
PassportModule, PassportModule,

39
api/src/auth/auth.service.ts

@ -14,6 +14,7 @@ import {
} from './schemas/password-reset.schema' } from './schemas/password-reset.schema'
import { MailService } from 'src/mail/mail.service' import { MailService } from 'src/mail/mail.service'
import { RequestResetPasswordInputDTO, ResetPasswordInputDTO } from './auth.dto' import { RequestResetPasswordInputDTO, ResetPasswordInputDTO } from './auth.dto'
import { AccessLog } from './schemas/access-log.schema'
@Injectable() @Injectable()
export class AuthService { export class AuthService {
constructor( constructor(
@ -22,6 +23,7 @@ export class AuthService {
@InjectModel(ApiKey.name) private apiKeyModel: Model<ApiKeyDocument>, @InjectModel(ApiKey.name) private apiKeyModel: Model<ApiKeyDocument>,
@InjectModel(PasswordReset.name) @InjectModel(PasswordReset.name)
private passwordResetModel: Model<PasswordResetDocument>, private passwordResetModel: Model<PasswordResetDocument>,
@InjectModel(AccessLog.name) private accessLogModel: Model<AccessLog>,
private readonly mailService: MailService, private readonly mailService: MailService,
) {} ) {}
@ -197,16 +199,35 @@ export class AuthService {
await this.apiKeyModel.deleteOne({ _id: apiKeyId }) await this.apiKeyModel.deleteOne({ _id: apiKeyId })
} }
async trackApiKeyUsage(apiKeyId: string) {
this.apiKeyModel
.findByIdAndUpdate(
apiKeyId,
{ $inc: { usageCount: 1 }, lastUsedAt: new Date() },
{ new: true },
)
.exec()
async trackAccessLog({ request }) {
const { apiKey, user, method, url, ip, headers } = request
const userAgent = headers['user-agent']
if (request.apiKey) {
this.apiKeyModel
.findByIdAndUpdate(
apiKey._id,
{ $inc: { usageCount: 1 }, lastUsedAt: new Date() },
{ new: true },
)
.exec()
.catch((e) => {
console.log('Failed to update api key usage count')
console.log(e)
})
}
this.accessLogModel
.create({
apiKey,
user,
method,
url: url.split('?')[0],
ip,
userAgent,
})
.catch((e) => { .catch((e) => {
console.log('Failed to track api key usage')
console.log('Failed to track access log')
console.log(e) console.log(e)
}) })
} }

3
api/src/auth/guards/auth.guard.ts

@ -42,7 +42,7 @@ export class AuthGuard implements CanActivate {
if (apiKey && bcrypt.compareSync(apiKeyString, apiKey.hashedApiKey)) { if (apiKey && bcrypt.compareSync(apiKeyString, apiKey.hashedApiKey)) {
userId = apiKey.user userId = apiKey.user
this.authService.trackApiKeyUsage(apiKey._id)
request.apiKey = apiKey
} }
} }
@ -50,6 +50,7 @@ export class AuthGuard implements CanActivate {
const user = await this.usersService.findOne({ _id: userId }) const user = await this.usersService.findOne({ _id: userId })
if (user) { if (user) {
request.user = user request.user = user
this.authService.trackAccessLog({ request })
return true return true
} }
} }

31
api/src/auth/schemas/access-log.schema.ts

@ -0,0 +1,31 @@
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'
import { Document, Types } from 'mongoose'
import { User } from '../../users/schemas/user.schema'
import { ApiKey } from './api-key.schema'
export type AccessLogDocument = AccessLog & Document
@Schema({ timestamps: true })
export class AccessLog {
_id?: Types.ObjectId
@Prop({ type: Types.ObjectId, ref: ApiKey.name })
apiKey: ApiKey
@Prop({ type: Types.ObjectId, ref: User.name })
user: User
@Prop({ type: String })
url: string
@Prop({ type: String })
method: string
@Prop({ type: String })
ip: string
@Prop({ type: String })
userAgent: string
}
export const AccessLogSchema = SchemaFactory.createForClass(AccessLog)
Loading…
Cancel
Save