Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ jobs:
cache: npm
cache-dependency-path: harvest-finance/backend/package-lock.json

- run: npm ci || true
- run: npm run build || true
- run: npm ci
- run: npm run build
- run: npm test -- --forceExit --passWithNoTests || true
- name: Upload coverage
uses: actions/upload-artifact@v4
Expand Down Expand Up @@ -130,8 +130,8 @@ jobs:
cache: npm
cache-dependency-path: harvest-finance/frontend/package-lock.json

- run: npm ci || true
- run: npm run lint || true
- run: npm ci
- run: npm run lint

frontend-test:
name: Frontend — Tests
Expand All @@ -148,9 +148,9 @@ jobs:
cache: npm
cache-dependency-path: harvest-finance/frontend/package-lock.json

- run: npm ci || true
- run: npm test -- --passWithNoTests || true
- run: npm run test:vitest -- --run --passWithNoTests || true
- run: npm ci
- run: npm test -- --passWithNoTests
- run: npm run test:vitest -- --run --passWithNoTests

frontend-build:
name: Frontend — Next.js Build
Expand Down
2 changes: 2 additions & 0 deletions harvest-finance/backend/src/admin/admin.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ import { Deposit } from '../database/entities/deposit.entity';
import { User } from '../database/entities/user.entity';
import { Reward } from '../database/entities/reward.entity';
import { Withdrawal } from '../database/entities/withdrawal.entity';
import { AuthModule } from '../auth/auth.module';

@Module({
imports: [
TypeOrmModule.forFeature([Vault, Deposit, User, Reward, Withdrawal]),
AuthModule,
],
controllers: [AdminController],
providers: [AdminService, EmailTemplatingService],
Expand Down
10 changes: 10 additions & 0 deletions harvest-finance/backend/src/admin/admin.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
import { DashboardStatsDto } from './dto/dashboard-stats.dto';
import { CreateVaultDto, UpdateVaultDto } from './dto/vault-crud.dto';
import { PlatformAnalyticsDto } from './dto/analytics.dto';
import { AuthService } from '../auth/auth.service';

@Injectable()
export class AdminService {
Expand All @@ -31,6 +32,7 @@ export class AdminService {
@InjectRepository(Withdrawal)
private withdrawalRepository: Repository<Withdrawal>,
private dataSource: DataSource,
private authService: AuthService,
) {}

/**
Expand Down Expand Up @@ -136,6 +138,14 @@ export class AdminService {
createVaultDto: CreateVaultDto,
adminId: string,
): Promise<Vault> {
// Check email verification
const isVerified = await this.authService.isEmailVerified(adminId);
if (!isVerified) {
throw new ForbiddenException(
'Email verification is required to create a vault. Please verify your email address.',
);
}

const vault = this.vaultRepository.create({
...createVaultDto,
ownerId: adminId,
Expand Down
21 changes: 15 additions & 6 deletions harvest-finance/backend/src/analytics/analytics.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,24 @@ import { TypeOrmModule } from '@nestjs/typeorm';

import { Vault } from '../database/entities/vault.entity';
import { Deposit } from '../database/entities/deposit.entity';
import { ScoringService } from './scoring.service';
import { RiskService } from './risk.service';
import { Withdrawal } from '../database/entities/withdrawal.entity';
import { VaultApyHistory } from '../database/entities/vault-apy-history.entity';
import { VaultScoreHistory } from '../database/entities/vault-score-history.entity';
import { AnalyticsService } from './analytics.service';
import { AnalyticsController } from './analytics.controller';
import { NotificationsModule } from '../notifications/notifications.module';
import { AnalyticsInterceptor } from './analytics.interceptor';
import { ScoringService } from './scoring.service';

@Module({
imports: [TypeOrmModule.forFeature([Vault, Deposit]), NotificationsModule],
providers: [ScoringService, RiskService],
imports: [
TypeOrmModule.forFeature([Vault, Deposit, Withdrawal, VaultApyHistory, VaultScoreHistory]),
],
controllers: [AnalyticsController],
exports: [ScoringService, RiskService],
providers: [
AnalyticsService,
ScoringService,
{ provide: APP_INTERCEPTOR, useClass: AnalyticsInterceptor },
],
exports: [AnalyticsService, ScoringService],
})
export class AnalyticsModule {}
9 changes: 2 additions & 7 deletions harvest-finance/backend/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ import { NotificationsModule } from './notifications/notifications.module';
import { RewardsModule } from './rewards/rewards.module';
import { ObservabilityModule } from './observability/observability.module';
import { AppConfigModule } from './config/config.module';
import { TelegramModule } from './integrations/telegram/telegram.module';

import {
Achievement,
Expand Down Expand Up @@ -94,7 +93,6 @@ import { CreateStrategyAndApyHistory1700000000017 } from './database/migrations/
import { CreateVaultScoreHistory1700000000018 } from './database/migrations/1700000000018-CreateVaultScoreHistory';

import { CreateVaultReservations1700000000018 } from './database/migrations/1700000000018-CreateVaultReservations';
import { AddDepositorConcentrationThreshold1700000000022 } from './database/migrations/1700000000022-AddDepositorConcentrationThreshold';
import { VaultReservation } from './vaults/entities/vault-reservation.entity';
import { Session } from './database/entities/session.entity';
import { SecurityEvent } from './database/entities/security-event.entity';
Expand All @@ -104,9 +102,6 @@ import { AddRefreshTokenRotation1700000000022 } from './database/migrations/1700
import { DomainEventsModule } from './domain-events';
import { DomainEventHandlersModule } from './common/events';
import { WebhooksModule } from './webhooks/webhooks.module';
import { WalletsModule } from './wallets/wallets.module';
import { CustodialWallet } from './wallets/entities/custodial-wallet.entity';
import { CreateCustodialWallets1700000000021 } from './database/migrations/1700000000021-CreateCustodialWallets';

@Module({
imports: [
Expand Down Expand Up @@ -153,7 +148,8 @@ import { CreateCustodialWallets1700000000021 } from './database/migrations/17000
YieldAnalytics,
Strategy,
VaultApyHistory,
CustodialWallet,
VaultScoreHistory,
VaultReservation,
],
migrations: [
CreateInitialSchema1700000000000,
Expand Down Expand Up @@ -216,7 +212,6 @@ import { CreateCustodialWallets1700000000021 } from './database/migrations/17000
StateSyncModule,
WebhooksModule,
DomainEventHandlersModule,
TelegramModule,
],
controllers: [AppController],
providers: [
Expand Down
20 changes: 20 additions & 0 deletions harvest-finance/backend/src/auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -421,12 +421,32 @@ export class AuthController {
}

@Get('verify-email')
@ApiOperation({
summary: 'Verify email address',
description: 'Verifies a user\'s email address using the JWT token sent via email. The token expires in 24 hours.',
})
@ApiResponse({ status: 200, description: 'Email verified successfully', schema: { type: 'object', properties: { success: { type: 'boolean' }, message: { type: 'string' } } } })
@ApiResponse({ status: 400, description: 'Invalid or expired token' })
async verifyEmail(@Query('token') token: string) {
return this.authService.verifyEmail(token);
}

@Post('resend-verification')
@UseGuards(JwtAuthGuard)
@UseGuards(RateLimitGuard)
@RateLimit({
limit: 3,
ttl: 3600,
message: 'Too many verification requests. Please try again in 1 hour.',
})
@ApiOperation({
summary: 'Resend verification email',
description: 'Resends the email verification link. Only available for unverified users. Rate limited to 3 requests per hour.',
})
@ApiResponse({ status: 200, description: 'Verification email sent', schema: { type: 'object', properties: { success: { type: 'boolean' }, message: { type: 'string' } } } })
@ApiResponse({ status: 400, description: 'User not found or already verified' })
@ApiResponse({ status: 401, description: 'Unauthorized' })
@ApiResponse({ status: 429, description: 'Too many requests' })
async resendVerification(@Req() req) {
return this.authService.resendVerification(req.user.id);
}
Expand Down
Loading
Loading