diff --git a/src/analytics/analytics.controller.ts b/src/analytics/analytics.controller.ts index 8b792e5..0123c2e 100644 --- a/src/analytics/analytics.controller.ts +++ b/src/analytics/analytics.controller.ts @@ -1,12 +1,15 @@ -import { Controller, Get, Param } from '@nestjs/common'; -import { ApiTags } from '@nestjs/swagger'; +import { Controller, Get, Param, UseGuards } from '@nestjs/common'; +import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; import { AnalyticsService } from './analytics.service'; +import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard'; @ApiTags('analytics') @Controller('analytics') export class AnalyticsController { constructor(private readonly analyticsService: AnalyticsService) {} + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) @Get('contributors/:userId') forContributor(@Param('userId') userId: string) { return this.analyticsService.forContributor(userId); diff --git a/src/common/entities/bounty.entity.ts b/src/common/entities/bounty.entity.ts index 74e35db..a9ff8d9 100644 --- a/src/common/entities/bounty.entity.ts +++ b/src/common/entities/bounty.entity.ts @@ -2,6 +2,7 @@ import { Column, CreateDateColumn, Entity, + Index, JoinColumn, ManyToOne, OneToOne, @@ -37,6 +38,7 @@ export class Bounty { @JoinColumn() claimedBy: User | null; + @Index() @Column({ type: 'varchar', nullable: true }) claimedById: string | null; diff --git a/src/common/entities/maintenance-pool.entity.ts b/src/common/entities/maintenance-pool.entity.ts index a956bcf..84e1ff9 100644 --- a/src/common/entities/maintenance-pool.entity.ts +++ b/src/common/entities/maintenance-pool.entity.ts @@ -18,7 +18,7 @@ export class MaintenancePool { @PrimaryGeneratedColumn('uuid') id: string; - @Column() + @Column({ type: 'varchar', length: 150 }) name: string; @ManyToOne(() => Repository, { onDelete: 'CASCADE', nullable: true }) diff --git a/src/common/entities/milestone.entity.ts b/src/common/entities/milestone.entity.ts index c06ccb1..84bc7f7 100644 --- a/src/common/entities/milestone.entity.ts +++ b/src/common/entities/milestone.entity.ts @@ -27,10 +27,10 @@ export class Milestone { @Column() repositoryId: string; - @Column() + @Column({ type: 'varchar', length: 255 }) title: string; - @Column({ type: 'text', nullable: true }) + @Column({ type: 'varchar', length: 2000, nullable: true }) description: string | null; @ManyToOne(() => User, { onDelete: 'SET NULL', nullable: true }) diff --git a/src/common/entities/team-member-split.entity.ts b/src/common/entities/team-member-split.entity.ts index 65482ee..a293f22 100644 --- a/src/common/entities/team-member-split.entity.ts +++ b/src/common/entities/team-member-split.entity.ts @@ -29,7 +29,7 @@ export class TeamMemberSplit { userId: string; /** Free-text label describing the member's contribution, e.g. "frontend". */ - @Column({ type: 'varchar', nullable: true }) + @Column({ type: 'varchar', length: 100, nullable: true }) role: string | null; /** Percentage of the bounty payout, 0-100. Sum across a team must equal 100. */ diff --git a/src/common/entities/webhook-event.entity.ts b/src/common/entities/webhook-event.entity.ts index dab53b5..e3f2fd6 100644 --- a/src/common/entities/webhook-event.entity.ts +++ b/src/common/entities/webhook-event.entity.ts @@ -2,11 +2,14 @@ import { Column, CreateDateColumn, Entity, + Index, PrimaryGeneratedColumn, } from 'typeorm'; import { WebhookEventStatus } from '../enums'; /** Audit log of every inbound webhook, verified or not, for replay/debugging. */ +@Index(['eventType', 'status']) +@Index(['status', 'receivedAt']) @Entity('webhook_events') export class WebhookEvent { @PrimaryGeneratedColumn('uuid') @@ -37,6 +40,7 @@ export class WebhookEvent { @Column({ type: 'text', nullable: true }) error: string | null; + @Index() @CreateDateColumn() receivedAt: Date; diff --git a/src/maintenance-pool/dto/create-pool.dto.ts b/src/maintenance-pool/dto/create-pool.dto.ts index 503f464..8e72da4 100644 --- a/src/maintenance-pool/dto/create-pool.dto.ts +++ b/src/maintenance-pool/dto/create-pool.dto.ts @@ -1,11 +1,12 @@ import { ApiProperty } from '@nestjs/swagger'; -import { IsOptional, IsString, IsUUID } from 'class-validator'; +import { IsOptional, IsString, IsUUID, MaxLength } from 'class-validator'; import { AssetType } from '../../common/enums'; import { IsSupportedEscrowAsset } from '../../common/validators/money.validator'; export class CreatePoolDto { @ApiProperty() @IsString() + @MaxLength(150) name: string; @ApiProperty({ required: false }) diff --git a/src/milestones/dto/create-milestone.dto.ts b/src/milestones/dto/create-milestone.dto.ts index d98ef88..e89b5af 100644 --- a/src/milestones/dto/create-milestone.dto.ts +++ b/src/milestones/dto/create-milestone.dto.ts @@ -1,5 +1,5 @@ import { ApiProperty } from '@nestjs/swagger'; -import { IsISO8601, IsOptional, IsString, IsUUID } from 'class-validator'; +import { IsISO8601, IsOptional, IsString, IsUUID, MaxLength } from 'class-validator'; import { AssetType } from '../../common/enums'; import { IsMoneyAmount, @@ -18,11 +18,13 @@ export class CreateMilestoneDto { @ApiProperty() @IsString() + @MaxLength(255) title: string; @ApiProperty({ required: false }) @IsOptional() @IsString() + @MaxLength(2000) description?: string; @ApiProperty() diff --git a/src/reputation/reputation.controller.ts b/src/reputation/reputation.controller.ts index 8647e80..2faec1e 100644 --- a/src/reputation/reputation.controller.ts +++ b/src/reputation/reputation.controller.ts @@ -1,22 +1,29 @@ -import { Controller, Get, Param, Post } from '@nestjs/common'; -import { ApiTags } from '@nestjs/swagger'; +import { Controller, Get, Param, Post, UseGuards } from '@nestjs/common'; +import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; import { ReputationService } from './reputation.service'; +import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard'; @ApiTags('reputation') @Controller('reputation') export class ReputationController { constructor(private readonly reputationService: ReputationService) {} + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) @Post(':userId/recompute') recompute(@Param('userId') userId: string) { return this.reputationService.computeAndSave(userId); } + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) @Get(':userId') latest(@Param('userId') userId: string) { return this.reputationService.getLatest(userId); } + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) @Get(':userId/history') history(@Param('userId') userId: string) { return this.reputationService.history(userId); diff --git a/src/teams/dto/create-team.dto.ts b/src/teams/dto/create-team.dto.ts index d9bb676..9a58bb6 100644 --- a/src/teams/dto/create-team.dto.ts +++ b/src/teams/dto/create-team.dto.ts @@ -6,6 +6,7 @@ import { IsOptional, IsString, IsUUID, + MaxLength, ValidateNested, } from 'class-validator'; @@ -17,6 +18,7 @@ export class TeamMemberSplitDto { @ApiProperty({ required: false, example: 'frontend' }) @IsOptional() @IsString() + @MaxLength(100) role?: string; @ApiProperty({ example: 40 }) @@ -27,6 +29,7 @@ export class TeamMemberSplitDto { export class CreateTeamDto { @ApiProperty() @IsString() + @MaxLength(150) name: string; @ApiProperty({ required: false })