Skip to content

Commit

Permalink
improve and fix api logic
Browse files Browse the repository at this point in the history
  • Loading branch information
marc-aurele-besner committed Jan 9, 2025
1 parent b6f5811 commit ab0b14e
Show file tree
Hide file tree
Showing 11 changed files with 217 additions and 61 deletions.
7 changes: 4 additions & 3 deletions indexers/api/src/auth/api-key.guard.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { ApiUsageLimitException } from '../exceptions/api-usage-limit.exception';

@Injectable()
export class ApiKeyGuard extends AuthGuard('api-key') {
handleRequest<TUser = any>(err: Error, user: TUser): TUser {
if (err || !user) {
throw new UnauthorizedException('Invalid API key');
}
if (err instanceof ApiUsageLimitException) throw err;
if (err || !user) throw new UnauthorizedException('Invalid API key');

return user;
}
}
13 changes: 7 additions & 6 deletions indexers/api/src/auth/api-key.strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { InjectRepository } from '@nestjs/typeorm';
import { HeaderAPIKeyStrategy } from 'passport-headerapikey';
import { Repository } from 'typeorm';
import { ApiKey } from '../entities/users/api-key.entity';
import { ApiUsageLimitException } from '../exceptions/api-usage-limit.exception';
import { ApiUsageService } from '../services/api-usage.service';

@Injectable()
Expand All @@ -17,6 +18,7 @@ export class ApiKeyStrategy extends PassportStrategy(
private apiUsageService: ApiUsageService,
) {
super({ header: 'X-API-KEY', prefix: '' }, true, async (apiKey, done) => {
console.log('apiKey', apiKey);
return this.validate(apiKey, done);
});
}
Expand All @@ -25,24 +27,23 @@ export class ApiKeyStrategy extends PassportStrategy(
apiKey: string,
done: (error: Error | null, data: ApiKey | boolean) => void,
) {
if (!apiKey) {
if (!apiKey)
return done(new UnauthorizedException('Missing API Key'), false);
}

try {
const key = await this.apiKeyRepository.findOne({
where: { id: apiKey },
where: { key: apiKey },
});

if (!key || key.total_requests_remaining <= 0) {
if (!key)
return done(new UnauthorizedException('Invalid API Key'), false);
}

// Track API usage after successful validation
await this.apiUsageService.trackUsage(apiKey);

return done(null, key);
} catch (error) {
if (error instanceof ApiUsageLimitException) return done(error, false);

return done(new UnauthorizedException('Invalid API Key'), false);
}
}
Expand Down
8 changes: 7 additions & 1 deletion indexers/api/src/controllers/profile.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,13 @@ export class ProfileController {
@ApiResponse({ status: 200, description: 'List of public profiles' })
async getPublicProfiles() {
return this.profileRepository.find({
select: ['id', 'name', 'account_id'],
select: [
'id',
'name',
'api_total_requests',
'api_daily_requests_limit',
'api_monthly_requests_limit',
],
});
}

Expand Down
12 changes: 12 additions & 0 deletions indexers/api/src/entities/users/api-daily-usage.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,18 @@ export class ApiDailyUsage extends BaseEntity {
@Column('numeric')
total_requests: number;

@Column('date', { default: () => 'CURRENT_DATE' })
date: Date;

@Column('timestamp with time zone', { default: () => 'now()' })
created_at: Date;

@Column('timestamp with time zone', { default: () => 'now()' })
updated_at: Date;

@Column('timestamp with time zone', { nullable: true })
deleted_at: Date | null;

@ManyToOne(() => Profile, (profile) => profile.dailyUsages)
@JoinColumn({ name: 'profile_id' })
profile: Profile;
Expand Down
6 changes: 3 additions & 3 deletions indexers/api/src/entities/users/api-key.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ export class ApiKey extends BaseEntity {
@Column('uuid')
profile_id: string;

@Column('uuid')
key: string;

@Column()
description: string;

@Column('numeric')
total_requests: number;

@Column('numeric')
total_requests_remaining: number;

@ManyToOne(() => Profile, (profile) => profile.apiKeys)
@JoinColumn({ name: 'profile_id' })
profile: Profile;
Expand Down
12 changes: 12 additions & 0 deletions indexers/api/src/entities/users/api-keys-daily-usage.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,18 @@ export class ApiKeysDailyUsage extends BaseEntity {
@Column('numeric')
total_requests: number;

@Column('date', { default: () => 'CURRENT_DATE' })
date: Date;

@Column('timestamp with time zone', { default: () => 'now()' })
created_at: Date;

@Column('timestamp with time zone', { default: () => 'now()' })
updated_at: Date;

@Column('timestamp with time zone', { nullable: true })
deleted_at: Date | null;

@ManyToOne(() => ApiKey, (apiKey) => apiKey.dailyUsages)
@JoinColumn({ name: 'api_key_id' })
apiKey: ApiKey;
Expand Down
12 changes: 12 additions & 0 deletions indexers/api/src/entities/users/api-keys-monthly-usage.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,18 @@ export class ApiKeysMonthlyUsage extends BaseEntity {
@Column('numeric')
total_requests: number;

@Column('date', { default: () => "date_trunc('month', CURRENT_DATE)" })
date: Date;

@Column('timestamp with time zone', { default: () => 'now()' })
created_at: Date;

@Column('timestamp with time zone', { default: () => 'now()' })
updated_at: Date;

@Column('timestamp with time zone', { nullable: true })
deleted_at: Date | null;

@ManyToOne(() => ApiKey, (apiKey) => apiKey.monthlyUsages)
@JoinColumn({ name: 'api_key_id' })
apiKey: ApiKey;
Expand Down
13 changes: 11 additions & 2 deletions indexers/api/src/entities/users/api-monthly-usage.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,17 @@ export class ApiMonthlyUsage extends BaseEntity {
@Column('numeric')
total_requests: number;

@Column('numeric')
total_requests_remaining: number;
@Column('date', { default: () => "date_trunc('month', CURRENT_DATE)" })
date: Date;

@Column('timestamp with time zone', { default: () => 'now()' })
created_at: Date;

@Column('timestamp with time zone', { default: () => 'now()' })
updated_at: Date;

@Column('timestamp with time zone', { nullable: true })
deleted_at: Date | null;

@ManyToOne(() => Profile, (profile) => profile.monthlyUsages)
@JoinColumn({ name: 'profile_id' })
Expand Down
17 changes: 13 additions & 4 deletions indexers/api/src/entities/users/profile.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ import { BaseEntity } from './base.entity';

@Entity('profiles', { schema: 'users' })
export class Profile extends BaseEntity {
@Column()
account_id: string;

@Column()
name: string;

Expand Down Expand Up @@ -76,7 +73,19 @@ export class Profile extends BaseEntity {
api_total_requests: number;

@Column('numeric')
api_total_requests_remaining: number;
api_daily_requests_limit: number;

@Column('numeric')
api_monthly_requests_limit: number;

@Column({ type: 'timestamp with time zone' })
created_at: Date;

@Column({ type: 'timestamp with time zone' })
updated_at: Date;

@Column({ type: 'timestamp with time zone', nullable: true })
deleted_at: Date | null;

@OneToMany(() => ApiKey, (apiKey) => apiKey.profile)
apiKeys: ApiKey[];
Expand Down
7 changes: 7 additions & 0 deletions indexers/api/src/exceptions/api-usage-limit.exception.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { HttpException, HttpStatus } from '@nestjs/common';

export class ApiUsageLimitException extends HttpException {
constructor(message: string) {
super(message, HttpStatus.TOO_MANY_REQUESTS);
}
}
Loading

0 comments on commit ab0b14e

Please sign in to comment.