From 9d263af250dcd11c59852bf1331838b66d808a9e Mon Sep 17 00:00:00 2001 From: Martin Young Date: Wed, 29 Jul 2026 23:18:10 +0000 Subject: [PATCH] Fix backend type-safety issues, contract workspace structure, and repo hygiene - Remove all 36 remaining @ts-nocheck/@ts-ignore suppressions from backend/src by fixing the underlying type errors (noUncheckedIndexedAccess violations, duplicate declarations, zod v4 error-option API mismatch, missing Prisma models for the bridge indexer, undefined redisClient/pubClient references, and an Express router variable name collision in routes/index.ts) (#946) - Fix ExplorerTransaction operation/asset type mismatch in blockExplorer.service.ts via a bounds-safe pick() helper (#962) - Add missing src/lib.rs entry points for multisig_wallet_timelock and did_registry so every Cargo workspace member follows the same layout, and confirm content_management_system and all other members are fully populated (#969, #970) - Update .gitignore (root, backend, frontend) to exclude test snapshots, coverage output, and stray generated contract check/error files --- .gitignore | 13 ++++++ backend/.gitignore | 7 ++++ .../migration.sql | 42 +++++++++++++++++++ backend/prisma/schema.prisma | 40 ++++++++++++++++++ .../src/blockchain/chain-indexer-engine.ts | 1 - backend/src/cache/BlockHeaderListener.ts | 3 +- backend/src/cache/CacheWarmer.ts | 3 +- backend/src/cache/DistributedCacheManager.ts | 11 +++-- .../src/certificates/CertificateService.ts | 1 - backend/src/config/region.config.ts | 4 +- backend/src/dashboard/hash.controller.ts | 1 - backend/src/index.ts | 1 - backend/src/infrastructure/p2p.controller.ts | 1 - backend/src/licenses/license.routes.ts | 1 - .../src/notifications/NotificationService.ts | 17 ++++---- .../src/notifications/notification.routes.ts | 1 - .../routes/contracts.validation.schemas.ts | 17 ++++---- backend/src/routes/courses.ts | 1 - .../routes/dependencies.validation.schemas.ts | 17 ++++---- .../src/routes/generator/explorer.routes.ts | 1 - .../src/routes/generator/generator.routes.ts | 1 - backend/src/routes/index.ts | 9 ++-- .../routes/playground/playground.routes.ts | 1 - backend/src/routes/simulatorErrors.routes.ts | 1 - backend/src/routes/storage.routes.ts | 1 - backend/src/routes/students.ts | 4 +- backend/src/services/anonymizationService.ts | 8 ++-- backend/src/services/blockExplorer.service.ts | 10 +++-- .../src/services/dependency-update.service.ts | 14 +++---- backend/src/services/gasEstimation.service.ts | 3 +- backend/src/services/rust-validation.ts | 3 +- .../src/services/seo/simulatorSeo.service.ts | 4 +- .../src/services/storage/asset.repository.ts | 1 - .../storage/providers/pinata.provider.ts | 1 - backend/src/services/storage/queue.ts | 3 -- .../src/services/storage/storage.service.ts | 5 +-- backend/src/services/storage/worker.ts | 1 - .../services/vulnerabilityScanner.service.ts | 3 +- backend/src/services/webhooks/queue.ts | 1 - backend/src/simulator/voting.controller.ts | 1 - backend/src/utils/audit.ts | 1 - contracts/did_registry/Cargo.toml | 1 - contracts/did_registry/{ => src}/lib.rs | 0 contracts/multisig_wallet_timelock/Cargo.toml | 1 - .../multisig_wallet_timelock/{ => src}/lib.rs | 0 frontend/.gitignore | 2 + 46 files changed, 168 insertions(+), 95 deletions(-) create mode 100644 backend/prisma/migrations/20260701000000_create_bridge_indexer_tables/migration.sql rename contracts/did_registry/{ => src}/lib.rs (100%) rename contracts/multisig_wallet_timelock/{ => src}/lib.rs (100%) diff --git a/.gitignore b/.gitignore index 6978029d..abb381c1 100644 --- a/.gitignore +++ b/.gitignore @@ -51,3 +51,16 @@ frontend/tsc_errors.log frontend/tsc_errors_new.log frontend/tsc_errors_new2.log contracts/test_output_activity.txt +contracts/test_output*.txt +contracts/check_output*.json +contracts/check_errors.txt +contracts/errors*.json + +# test snapshots +**/__snapshots__/ +*.snap + +# editor/OS junk +.vscode/ +.idea/ +*.swp diff --git a/backend/.gitignore b/backend/.gitignore index d9cf47f3..b277a3dc 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -3,3 +3,10 @@ node_modules .env /src/generated/prismalogs/ + +# test snapshots and coverage output +**/__snapshots__/ +*.snap +coverage/ +dist/ +*.log diff --git a/backend/prisma/migrations/20260701000000_create_bridge_indexer_tables/migration.sql b/backend/prisma/migrations/20260701000000_create_bridge_indexer_tables/migration.sql new file mode 100644 index 00000000..cef052a2 --- /dev/null +++ b/backend/prisma/migrations/20260701000000_create_bridge_indexer_tables/migration.sql @@ -0,0 +1,42 @@ +-- CreateTable: ProcessedBlock and BridgeEvent for the cross-chain bridge indexer +CREATE TABLE "processed_blocks" ( + "id" TEXT NOT NULL, + "chain" TEXT NOT NULL, + "blockNumber" INTEGER NOT NULL, + "blockHash" TEXT NOT NULL, + "parentHash" TEXT NOT NULL, + "timestamp" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "processedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "isRolledBack" BOOLEAN NOT NULL DEFAULT false, + "rolledBackAt" TIMESTAMP(3), + + CONSTRAINT "processed_blocks_pkey" PRIMARY KEY ("id") +); + +CREATE TABLE "bridge_events" ( + "id" TEXT NOT NULL, + "chain" TEXT NOT NULL, + "blockNumber" INTEGER NOT NULL, + "blockHash" TEXT NOT NULL, + "processedBlockId" TEXT NOT NULL, + "eventId" TEXT NOT NULL, + "eventType" TEXT NOT NULL, + "sourceChain" TEXT NOT NULL, + "targetChain" TEXT NOT NULL, + "data" JSONB NOT NULL, + "transactionHash" TEXT, + "logIndex" INTEGER, + "processed" BOOLEAN NOT NULL DEFAULT false, + "processedAt" TIMESTAMP(3), + + CONSTRAINT "bridge_events_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "processed_blocks_blockHash_key" ON "processed_blocks"("blockHash"); +CREATE INDEX "processed_blocks_chain_blockNumber_idx" ON "processed_blocks"("chain", "blockNumber"); +CREATE UNIQUE INDEX "bridge_events_chain_eventId_key" ON "bridge_events"("chain", "eventId"); +CREATE INDEX "bridge_events_processedBlockId_idx" ON "bridge_events"("processedBlockId"); + +-- AddForeignKey +ALTER TABLE "bridge_events" ADD CONSTRAINT "bridge_events_processedBlockId_fkey" FOREIGN KEY ("processedBlockId") REFERENCES "processed_blocks"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma index ca522db0..32f077b1 100644 --- a/backend/prisma/schema.prisma +++ b/backend/prisma/schema.prisma @@ -427,3 +427,43 @@ model P2PMessage { @@index([receiverId]) @@map("p2p_messages") } + +model ProcessedBlock { + id String @id @default(cuid()) + chain String + blockNumber Int + blockHash String @unique + parentHash String + timestamp DateTime @default(now()) + processedAt DateTime @default(now()) + isRolledBack Boolean @default(false) + rolledBackAt DateTime? + + bridgeEvents BridgeEvent[] + + @@index([chain, blockNumber]) + @@map("processed_blocks") +} + +model BridgeEvent { + id String @id @default(cuid()) + chain String + blockNumber Int + blockHash String + processedBlockId String + eventId String + eventType String + sourceChain String + targetChain String + data Json + transactionHash String? + logIndex Int? + processed Boolean @default(false) + processedAt DateTime? + + processedBlock ProcessedBlock @relation(fields: [processedBlockId], references: [id], onDelete: Cascade) + + @@unique([chain, eventId]) + @@index([processedBlockId]) + @@map("bridge_events") +} diff --git a/backend/src/blockchain/chain-indexer-engine.ts b/backend/src/blockchain/chain-indexer-engine.ts index 23eb794b..9d519458 100644 --- a/backend/src/blockchain/chain-indexer-engine.ts +++ b/backend/src/blockchain/chain-indexer-engine.ts @@ -1,4 +1,3 @@ -// @ts-nocheck /** * Cross-Chain Bridge Event Indexer Engine * Handles block indexing, event processing, and re-org detection for dual-chain bridges diff --git a/backend/src/cache/BlockHeaderListener.ts b/backend/src/cache/BlockHeaderListener.ts index 3715855c..2cefa2d9 100644 --- a/backend/src/cache/BlockHeaderListener.ts +++ b/backend/src/cache/BlockHeaderListener.ts @@ -1,4 +1,3 @@ -// @ts-nocheck import { EventEmitter } from 'events'; import logger from '../utils/logger.js'; import cacheService from './CacheService.js'; @@ -18,7 +17,7 @@ export interface BlockHeader { export class BlockHeaderListener extends EventEmitter { private isListening = false; private lastBlockHeight = 0; - private pollingInterval: NodeJS.Timer | null = null; + private pollingInterval: NodeJS.Timeout | null = null; private readonly POLL_INTERVAL = parseInt(process.env.BLOCK_POLL_INTERVAL || '10000', 10); // 10 seconds default /** diff --git a/backend/src/cache/CacheWarmer.ts b/backend/src/cache/CacheWarmer.ts index 1275b08e..43136af2 100644 --- a/backend/src/cache/CacheWarmer.ts +++ b/backend/src/cache/CacheWarmer.ts @@ -1,4 +1,3 @@ -// @ts-nocheck import { cacheTTL } from '../config/redis.config.js'; import logger from '../utils/logger.js'; import cacheService, { CACHE_KEYS } from './CacheService.js'; @@ -9,7 +8,7 @@ import cacheService, { CACHE_KEYS } from './CacheService.js'; */ export class CacheWarmer { private isWarming = false; - private warmingInterval: NodeJS.Timer | null = null; + private warmingInterval: NodeJS.Timeout | null = null; private readonly WARMING_INTERVAL = parseInt(process.env.CACHE_WARMING_INTERVAL || '300000', 10); // 5 minutes /** diff --git a/backend/src/cache/DistributedCacheManager.ts b/backend/src/cache/DistributedCacheManager.ts index 9285332d..9a4c9384 100644 --- a/backend/src/cache/DistributedCacheManager.ts +++ b/backend/src/cache/DistributedCacheManager.ts @@ -1,4 +1,3 @@ -// @ts-nocheck import logger from '../utils/logger.js'; import cacheService from './CacheService.js'; import redisClient from './RedisClient.js'; @@ -135,18 +134,18 @@ export class DistributedCacheManager { let memoryUsage = 'N/A'; lines.forEach((line) => { - if (line.includes('keyspace_hits')) hits = parseInt(line.split(':')[1]); - if (line.includes('keyspace_misses')) misses = parseInt(line.split(':')[1]); - if (line.includes('evicted_keys')) evictions = parseInt(line.split(':')[1]); + if (line.includes('keyspace_hits')) hits = parseInt(line.split(':')[1] ?? '0', 10); + if (line.includes('keyspace_misses')) misses = parseInt(line.split(':')[1] ?? '0', 10); + if (line.includes('evicted_keys')) evictions = parseInt(line.split(':')[1] ?? '0', 10); }); memoryLines.forEach((line) => { - if (line.includes('used_memory_human')) memoryUsage = line.split(':')[1]; + if (line.includes('used_memory_human')) memoryUsage = line.split(':')[1] ?? 'N/A'; }); const keyspaceInfo = await client.info('keyspace'); const dbMatch = keyspaceInfo.match(/keys=(\d+)/); - if (dbMatch) keyspace = parseInt(dbMatch[1]); + if (dbMatch) keyspace = parseInt(dbMatch[1] ?? '0', 10); const hitRate = hits + misses > 0 ? (hits / (hits + misses)) * 100 : 0; diff --git a/backend/src/certificates/CertificateService.ts b/backend/src/certificates/CertificateService.ts index f7fb5234..314c1926 100644 --- a/backend/src/certificates/CertificateService.ts +++ b/backend/src/certificates/CertificateService.ts @@ -1,4 +1,3 @@ -// @ts-nocheck import prisma from '../db/index.js'; import { Certificate, diff --git a/backend/src/config/region.config.ts b/backend/src/config/region.config.ts index 241f7ce0..77dfe162 100644 --- a/backend/src/config/region.config.ts +++ b/backend/src/config/region.config.ts @@ -1,4 +1,3 @@ -// @ts-nocheck /** * Multi-region Redis replication configuration. * @@ -68,7 +67,8 @@ export function resolveActiveRegionName( if (requested && regions.some((r) => r.name === requested)) { return requested; } - return regions[0].name; + // Length checked above, so the first element is guaranteed to exist. + return regions[0]!.name; } /** diff --git a/backend/src/dashboard/hash.controller.ts b/backend/src/dashboard/hash.controller.ts index f19309d8..c3b37647 100644 --- a/backend/src/dashboard/hash.controller.ts +++ b/backend/src/dashboard/hash.controller.ts @@ -1,4 +1,3 @@ -// @ts-nocheck import { Request, Response } from 'express'; import { HashService } from './hash.service.js'; diff --git a/backend/src/index.ts b/backend/src/index.ts index d6bd0dae..8f4e343e 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -1,4 +1,3 @@ -// @ts-nocheck import cors from 'cors'; import express, { Request, Response } from 'express'; import { createServer } from 'http'; diff --git a/backend/src/infrastructure/p2p.controller.ts b/backend/src/infrastructure/p2p.controller.ts index 4284b4c5..77e8b154 100644 --- a/backend/src/infrastructure/p2p.controller.ts +++ b/backend/src/infrastructure/p2p.controller.ts @@ -1,4 +1,3 @@ -// @ts-nocheck import { Request, Response } from 'express'; import { P2PService } from './p2p.service.js'; diff --git a/backend/src/licenses/license.routes.ts b/backend/src/licenses/license.routes.ts index 5d713473..04f102fb 100644 --- a/backend/src/licenses/license.routes.ts +++ b/backend/src/licenses/license.routes.ts @@ -1,4 +1,3 @@ -// @ts-nocheck /** * Open Source License Guide - Express Routes * diff --git a/backend/src/notifications/NotificationService.ts b/backend/src/notifications/NotificationService.ts index 2c737f3a..5ba5655e 100644 --- a/backend/src/notifications/NotificationService.ts +++ b/backend/src/notifications/NotificationService.ts @@ -1,5 +1,5 @@ -// @ts-nocheck import logger from '../utils/logger.js'; +import redisClient from '../cache/RedisClient.js'; import { CourseNotification, CreateCourseNotificationDto, @@ -68,7 +68,8 @@ export async function createNotification( // Broadcast so all server instances & connected WebSocket clients receive it try { - await pubClient.publish('course_notifications', JSON.stringify(notification)); + const pubClient = redisClient.getPubClient(); + await pubClient?.publish('course_notifications', JSON.stringify(notification)); } catch (err) { logger.warn('Failed to publish course_notification to Redis:', err); } @@ -105,7 +106,7 @@ export function markAsRead(notificationId: string): boolean { for (const [, notifications] of store.entries()) { const idx = notifications.findIndex((n) => n.id === notificationId); if (idx !== -1) { - notifications[idx] = { ...notifications[idx], read: true }; + notifications[idx] = { ...notifications[idx]!, read: true }; return true; } } @@ -125,8 +126,8 @@ export function markAllAsRead(userId: string): number { const notifs = store.get(key); if (notifs) { for (let i = 0; i < notifs.length; i++) { - if (!notifs[i].read) { - notifs[i] = { ...notifs[i], read: true }; + if (!notifs[i]!.read) { + notifs[i] = { ...notifs[i]!, read: true }; count++; } } @@ -152,11 +153,11 @@ function mergeSorted( let j = 0; while (result.length < max && (i < a.length || j < b.length)) { if (i >= a.length) { - result.push(b[j++]); + result.push(b[j++]!); } else if (j >= b.length) { - result.push(a[i++]); + result.push(a[i++]!); } else { - result.push(a[i].createdAt >= b[j].createdAt ? a[i++] : b[j++]); + result.push(a[i]!.createdAt >= b[j]!.createdAt ? a[i++]! : b[j++]!); } } return result; diff --git a/backend/src/notifications/notification.routes.ts b/backend/src/notifications/notification.routes.ts index 8683b2ac..828754a2 100644 --- a/backend/src/notifications/notification.routes.ts +++ b/backend/src/notifications/notification.routes.ts @@ -1,4 +1,3 @@ -// @ts-nocheck import { Router, Request, Response } from 'express'; import { getNotifications, diff --git a/backend/src/routes/contracts.validation.schemas.ts b/backend/src/routes/contracts.validation.schemas.ts index c44d37bf..e62eb4c9 100644 --- a/backend/src/routes/contracts.validation.schemas.ts +++ b/backend/src/routes/contracts.validation.schemas.ts @@ -1,26 +1,25 @@ -// @ts-nocheck import { z } from 'zod'; export const contractCompileSchema = z.object({ sourceCode: z .string() - .min(32, { message: 'Contract source must contain at least 32 characters.' }) - .max(15000, { message: 'Contract source must not exceed 15,000 characters.' }), + .min(32, 'Contract source must contain at least 32 characters.') + .max(15000, 'Contract source must not exceed 15,000 characters.'), compilerVersion: z .string() - .regex(/^\d+\.\d+\.\d+$/, { message: 'Compiler version must follow semantic versioning, e.g. 0.8.10' }), + .regex(/^\d+\.\d+\.\d+$/, 'Compiler version must follow semantic versioning, e.g. 0.8.10'), optimization: z.boolean().default(false), target: z.enum(['solidity', 'evm', 'soroban', 'wasm']), entryPoint: z.string().max(128).optional(), }); export const contractExecutionSchema = z.object({ - contractAddress: z.string().min(32, { message: 'Contract address is required.' }), - functionName: z.string().min(1, { message: 'Function name is required.' }), + contractAddress: z.string().min(32, 'Contract address is required.'), + functionName: z.string().min(1, 'Function name is required.'), parameters: z - .array(z.union([z.string(), z.number(), z.boolean(), z.null()]), { invalid_type_error: 'Parameter values must be primitive types.' }) - .max(50, { message: 'Maximum of 50 parameters allowed.' }) + .array(z.union([z.string(), z.number(), z.boolean(), z.null()])) + .max(50, 'Maximum of 50 parameters allowed.') .optional(), - gasLimit: z.number().int().positive().max(10_000_000, { message: 'Gas limit must be positive and no more than 10,000,000.' }), + gasLimit: z.number().int().positive().max(10_000_000, 'Gas limit must be positive and no more than 10,000,000.'), caller: z.string().max(128).optional(), }); diff --git a/backend/src/routes/courses.ts b/backend/src/routes/courses.ts index 5ee9fc17..78bf1abb 100644 --- a/backend/src/routes/courses.ts +++ b/backend/src/routes/courses.ts @@ -1,4 +1,3 @@ -// @ts-nocheck import { Router } from 'express'; import { cacheMiddleware } from '../cache/CacheMiddleware.js'; import { invalidateAllCourses, invalidateCourseCache } from '../cache/CacheInvalidation.js'; diff --git a/backend/src/routes/dependencies.validation.schemas.ts b/backend/src/routes/dependencies.validation.schemas.ts index ae127e9d..21c03395 100644 --- a/backend/src/routes/dependencies.validation.schemas.ts +++ b/backend/src/routes/dependencies.validation.schemas.ts @@ -1,22 +1,19 @@ -// @ts-nocheck import { z } from 'zod'; export const dependencyCheckSchema = z.object({ cargoToml: z .string() - .min(1, { message: 'Cargo.toml content is required.' }) - .max(50_000, { message: 'Cargo.toml must not exceed 50,000 characters.' }), + .min(1, 'Cargo.toml content is required.') + .max(50_000, 'Cargo.toml must not exceed 50,000 characters.'), }); export const dependencyUpdateSchema = z.object({ cargoToml: z .string() - .min(1, { message: 'Cargo.toml content is required.' }) - .max(50_000, { message: 'Cargo.toml must not exceed 50,000 characters.' }), + .min(1, 'Cargo.toml content is required.') + .max(50_000, 'Cargo.toml must not exceed 50,000 characters.'), dependencies: z - .array(z.string().min(1).max(128), { - invalid_type_error: 'Dependencies must be an array of strings.', - }) - .min(1, { message: 'At least one dependency name is required.' }) - .max(100, { message: 'Cannot update more than 100 dependencies at once.' }), + .array(z.string().min(1).max(128)) + .min(1, 'At least one dependency name is required.') + .max(100, 'Cannot update more than 100 dependencies at once.'), }); diff --git a/backend/src/routes/generator/explorer.routes.ts b/backend/src/routes/generator/explorer.routes.ts index 0e34d49a..6a1e2d71 100644 --- a/backend/src/routes/generator/explorer.routes.ts +++ b/backend/src/routes/generator/explorer.routes.ts @@ -1,4 +1,3 @@ -// @ts-nocheck import { Router, Request, Response } from 'express'; import { filterTransactions, diff --git a/backend/src/routes/generator/generator.routes.ts b/backend/src/routes/generator/generator.routes.ts index 3a5556d2..39e7eea5 100644 --- a/backend/src/routes/generator/generator.routes.ts +++ b/backend/src/routes/generator/generator.routes.ts @@ -1,4 +1,3 @@ -// @ts-nocheck import { randomUUID } from 'crypto'; import { Request, Response, Router } from 'express'; import { GeneratorService } from '../../generator/generator.service.js'; diff --git a/backend/src/routes/index.ts b/backend/src/routes/index.ts index e94446f4..143606f5 100644 --- a/backend/src/routes/index.ts +++ b/backend/src/routes/index.ts @@ -1,4 +1,3 @@ -// @ts-nocheck import { Router } from 'express'; import dashboardRoutes from '../dashboard/dashboard.routes.js'; import activityLogRouter from '../dashboard/activityLog.routes.js'; @@ -27,7 +26,7 @@ import studentsRouter from './students.js'; import simulatorErrorsRouter from './simulatorErrors.routes.js'; import termsOfServiceRouter from './termsOfService.routes.js'; import privacyPolicyRouter from './privacyPolicy.routes.js'; -import playgroundRouter from './playground.routes.js'; +import playgroundValidateRouter from './playground.routes.js'; import oauthRouter from './oauth.routes.js'; import notificationRouter from '../notifications/notification.routes.js'; @@ -35,7 +34,7 @@ import notificationPreferencesRouter from '../notifications/preferences.routes.j import metricsRouter from './metrics.routes.js'; import dependenciesRouter from './dependencies.routes.js'; import infrastructureRouter from '../infrastructure/infrastructure.routes.js'; -import simulatorRouter from '../simulator/simulator.routes.js'; +import simulatorIdeasRouter from '../simulator/simulator.routes.js'; import webhooksRouter from './webhooks.js'; import adminDLQRouter from './admin/dlq.routes.js'; @@ -73,10 +72,10 @@ router.use('/user', userRouter); router.use('/metrics', metricsRouter); router.use('/dependencies', dependenciesRouter); router.use('/infrastructure', infrastructureRouter); -router.use('/simulator', simulatorRouter); +router.use('/simulator', simulatorIdeasRouter); router.use('/simulator/errors', simulatorErrorsRouter); router.use('/roadmap/tos', termsOfServiceRouter); -router.use('/playground', playgroundRouter); +router.use('/playground', playgroundValidateRouter); router.use('/playground/privacy-policy', privacyPolicyRouter); router.use('/oauth', oauthRouter); diff --git a/backend/src/routes/playground/playground.routes.ts b/backend/src/routes/playground/playground.routes.ts index 77f655a2..68c86bc5 100644 --- a/backend/src/routes/playground/playground.routes.ts +++ b/backend/src/routes/playground/playground.routes.ts @@ -1,4 +1,3 @@ -// @ts-nocheck import { Router, Request, Response } from 'express'; import { TRIAGE_SCENARIOS, diff --git a/backend/src/routes/simulatorErrors.routes.ts b/backend/src/routes/simulatorErrors.routes.ts index 49d41917..9fd0b340 100644 --- a/backend/src/routes/simulatorErrors.routes.ts +++ b/backend/src/routes/simulatorErrors.routes.ts @@ -1,4 +1,3 @@ -// @ts-nocheck import { Request, Response, Router } from 'express'; import { authenticate } from '../auth/auth.middleware.js'; import logger from '../utils/logger.js'; diff --git a/backend/src/routes/storage.routes.ts b/backend/src/routes/storage.routes.ts index d17e6843..e8e7a5ed 100644 --- a/backend/src/routes/storage.routes.ts +++ b/backend/src/routes/storage.routes.ts @@ -1,4 +1,3 @@ -// @ts-nocheck import { Request, Response, Router } from 'express'; import logger from '../utils/logger.js'; import { storageService } from '../services/storage/index.js'; diff --git a/backend/src/routes/students.ts b/backend/src/routes/students.ts index 0c5bd747..fd7c9b0a 100644 --- a/backend/src/routes/students.ts +++ b/backend/src/routes/students.ts @@ -1,4 +1,4 @@ -// @ts-nocheck +import type { Prisma } from '@prisma/client'; import { Router } from 'express'; import { normalizeSorobanDid } from '../auth/auth.service.js'; import { invalidateUserCache } from '../cache/CacheInvalidation.js'; @@ -111,7 +111,7 @@ router.put('/:id', auditAction('UPDATE_STUDENT', 'Student'), auditAction('UPDATE const { email, firstName, lastName, did } = req.body; const normalizedDid = normalizeSorobanDid(did); - const updateData: Record = { + const updateData: Prisma.StudentUpdateInput = { email, firstName, lastName, diff --git a/backend/src/services/anonymizationService.ts b/backend/src/services/anonymizationService.ts index 879c8a3e..426d2b42 100644 --- a/backend/src/services/anonymizationService.ts +++ b/backend/src/services/anonymizationService.ts @@ -1,5 +1,5 @@ -// @ts-nocheck import crypto from 'crypto'; +import type { Prisma } from '@prisma/client'; import prisma from '../db/index.js'; import logger from '../utils/logger.js'; @@ -37,9 +37,9 @@ class AnonymizationService { // 2. Clear existing (or move to archive if needed) analytics data // For simplicity, we'll just add new records or clear and reload - await (prisma as any).analyticsData.deleteMany({}); + await prisma.analyticsData.deleteMany({}); - const analyticsBatch = []; + const analyticsBatch: Prisma.AnalyticsDataCreateManyInput[] = []; for (const student of students) { // Anonymize user @@ -71,7 +71,7 @@ class AnonymizationService { // 3. Load sanitized data into analytics table if (analyticsBatch.length > 0) { - await (prisma as any).analyticsData.createMany({ + await prisma.analyticsData.createMany({ data: analyticsBatch, }); } diff --git a/backend/src/services/blockExplorer.service.ts b/backend/src/services/blockExplorer.service.ts index c2a4214c..defc6ec0 100644 --- a/backend/src/services/blockExplorer.service.ts +++ b/backend/src/services/blockExplorer.service.ts @@ -1,4 +1,3 @@ -// @ts-nocheck /** * Block Explorer Service — Hackathon Project Idea Generator backend. * @@ -45,6 +44,11 @@ function seededRandom(seed: number): () => number { }; } +function pick(options: readonly T[], rand: () => number): T { + const idx = Math.min(options.length - 1, Math.floor(rand() * options.length)); + return options[idx] as T; +} + function generateTransactions(count: number, seed: number, startLedger: number): ExplorerTransaction[] { const rand = seededRandom(seed); return Array.from({ length: count }, (_, i) => { @@ -55,9 +59,9 @@ function generateTransactions(count: number, seed: number, startLedger: number): hash: `H${seed.toString(16).padStart(8, '0')}${i.toString(16).padStart(8, '0')}`, source: `G${Math.floor(rand() * 1e10).toString(36).toUpperCase().padStart(10, '0')}`, destination: `G${Math.floor(rand() * 1e10).toString(36).toUpperCase().padStart(10, '0')}`, - operation: OPS[Math.floor(rand() * OPS.length)], + operation: pick(OPS, rand), amount: (rand() * 1000).toFixed(2), - asset: ASSETS[Math.floor(rand() * ASSETS.length)], + asset: pick(ASSETS, rand), fee: (100 + Math.floor(rand() * 900)).toString(), ledger, status, diff --git a/backend/src/services/dependency-update.service.ts b/backend/src/services/dependency-update.service.ts index 846b4456..29ce530d 100644 --- a/backend/src/services/dependency-update.service.ts +++ b/backend/src/services/dependency-update.service.ts @@ -1,4 +1,3 @@ -// @ts-nocheck import logger from '../utils/logger.js'; export interface CargoTomlDependency { @@ -47,8 +46,8 @@ const RELEASE_NOTES: Record = { }; function compareVersions(a: string, b: string): 'major' | 'minor' | 'patch' | 'none' { - const [aMaj, aMin, aPat] = a.split('.').map(Number); - const [bMaj, bMin, bPat] = b.split('.').map(Number); + const [aMaj = 0, aMin = 0, aPat = 0] = a.split('.').map(Number); + const [bMaj = 0, bMin = 0, bPat = 0] = b.split('.').map(Number); if (bMaj > aMaj) return 'major'; if (bMin > aMin) return 'minor'; if (bPat > aPat) return 'patch'; @@ -75,7 +74,7 @@ export function parseCargoTomlDependencies(cargoToml: string): Array<{ name: str const sectionMatch = cargoToml.match(/\[dependencies\]([\s\S]*?)(?=\n\[|$)/); if (!sectionMatch) return deps; - const section = sectionMatch[1]; + const section = sectionMatch[1] ?? ''; const lines = section.split('\n'); for (const line of lines) { @@ -85,14 +84,14 @@ export function parseCargoTomlDependencies(cargoToml: string): Array<{ name: str // Simple: name = "version" const simpleMatch = trimmed.match(/^([\w-]+)\s*=\s*"([^"]+)"/); if (simpleMatch) { - deps.push({ name: simpleMatch[1], version: simpleMatch[2] }); + deps.push({ name: simpleMatch[1]!, version: simpleMatch[2]! }); continue; } // Inline table: name = { version = "...", ... } const tableMatch = trimmed.match(/^([\w-]+)\s*=\s*\{[^}]*version\s*=\s*"([^"]+)"/); if (tableMatch) { - deps.push({ name: tableMatch[1], version: tableMatch[2] }); + deps.push({ name: tableMatch[1]!, version: tableMatch[2]! }); } } @@ -106,13 +105,14 @@ export async function checkDependencies(cargoToml: string): Promise { const latestVersion = REGISTRY[name] ?? version; const updateType = compareVersions(version, latestVersion); + const releaseNotes = RELEASE_NOTES[name]; return { name, currentVersion: version, latestVersion, isOutdated: updateType !== 'none', updateType, - ...(RELEASE_NOTES[name] ? { releaseNotes: RELEASE_NOTES[name] } : {}), + ...(releaseNotes ? { releaseNotes } : {}), }; }); diff --git a/backend/src/services/gasEstimation.service.ts b/backend/src/services/gasEstimation.service.ts index 3aa1202f..14d31b46 100644 --- a/backend/src/services/gasEstimation.service.ts +++ b/backend/src/services/gasEstimation.service.ts @@ -1,4 +1,3 @@ -// @ts-nocheck /** * Gas Estimation Service — Open Source Contribution Trainer backend. * @@ -89,7 +88,7 @@ export function estimateGas(request: GasEstimateRequest): GasEstimateResponse { if (!withinBudget) { recommendation = 'Gas exceeds budget — optimize storage writes and nested loops before submitting a PR.'; } else if (core.warnings.length > 0) { - recommendation = `Within budget but review ${core.warnings[0].metric} warnings before merge.`; + recommendation = `Within budget but review ${core.warnings[0]!.metric} warnings before merge.`; } return { diff --git a/backend/src/services/rust-validation.ts b/backend/src/services/rust-validation.ts index 95810136..1630272d 100644 --- a/backend/src/services/rust-validation.ts +++ b/backend/src/services/rust-validation.ts @@ -1,4 +1,3 @@ -// @ts-nocheck interface ValidationDiagnostic { line: number; column: number; @@ -24,7 +23,7 @@ export class RustValidationService { lines.forEach((line, index) => { const lineNumber = index + 1; for (let columnIndex = 0; columnIndex < line.length; columnIndex += 1) { - const char = line[columnIndex]; + const char = line[columnIndex]!; if (Object.prototype.hasOwnProperty.call(pairs, char)) { stack.push({ char, line: lineNumber, column: columnIndex + 1 }); continue; diff --git a/backend/src/services/seo/simulatorSeo.service.ts b/backend/src/services/seo/simulatorSeo.service.ts index c65c9221..e40dfbe5 100644 --- a/backend/src/services/seo/simulatorSeo.service.ts +++ b/backend/src/services/seo/simulatorSeo.service.ts @@ -1,4 +1,4 @@ -// @ts-nocheck +import redisClient from '../../cache/RedisClient.js'; export interface SimulatorAsset { slug: string; @@ -71,7 +71,7 @@ const SITEMAP_CACHE_KEY = 'seo:simulator:sitemap:v1'; function parseSitemapXml(xml: string): string[] { const matches = xml.matchAll(/(.*?)<\/loc>/g); - return Array.from(matches, (match) => match[1].trim()).filter(Boolean); + return Array.from(matches, (match) => (match[1] ?? '').trim()).filter(Boolean); } function buildMetaTags(asset: SimulatorAsset, baseUrl: string): SimulatorMetaTags { diff --git a/backend/src/services/storage/asset.repository.ts b/backend/src/services/storage/asset.repository.ts index ef69fa82..0361e512 100644 --- a/backend/src/services/storage/asset.repository.ts +++ b/backend/src/services/storage/asset.repository.ts @@ -1,4 +1,3 @@ -// @ts-nocheck import type { StorageAssetRecord } from './types.js'; const getPrisma = async () => { diff --git a/backend/src/services/storage/providers/pinata.provider.ts b/backend/src/services/storage/providers/pinata.provider.ts index 67b38773..fab659dd 100644 --- a/backend/src/services/storage/providers/pinata.provider.ts +++ b/backend/src/services/storage/providers/pinata.provider.ts @@ -1,4 +1,3 @@ -// @ts-nocheck import { canonicalizeJson, createDeterministicCid, buildGatewayUrl } from '../utils.js'; import type { StoragePinResult, StorageProvider } from '../types.js'; diff --git a/backend/src/services/storage/queue.ts b/backend/src/services/storage/queue.ts index 456e6415..1f75982c 100644 --- a/backend/src/services/storage/queue.ts +++ b/backend/src/services/storage/queue.ts @@ -1,4 +1,3 @@ -// @ts-nocheck import type { JobsOptions } from 'bullmq'; import { Queue } from 'bullmq'; import type { StorageGcJobData, StoragePinJobData } from './types.js'; @@ -20,7 +19,6 @@ const defaultPinJobOptions: JobsOptions = { age: 7 * 24 * 60 * 60, count: 1000, }, - timeout: Number(process.env.STORAGE_JOB_TIMEOUT_MS || '30000'), }; const createQueue = (name: string, defaultJobOptions?: JobsOptions) => { @@ -34,7 +32,6 @@ const createQueue = (name: string, defaultJobOptions?: JobsOptions) => { const redisUrl = new URL(process.env.REDIS_URL || (() => { throw new Error('REDIS_URL environment variable is required'); })()); - const redisUrl = new URL(process.env.REDIS_URL || 'redis://localhost:6379'); return new Queue(name, { connection: { diff --git a/backend/src/services/storage/storage.service.ts b/backend/src/services/storage/storage.service.ts index 5e9b8ca4..c8cec257 100644 --- a/backend/src/services/storage/storage.service.ts +++ b/backend/src/services/storage/storage.service.ts @@ -1,4 +1,3 @@ -// @ts-nocheck import { storageGcQueue, storagePinQueue } from './queue.js'; import { createStorageProvider } from './provider.js'; import { buildGatewayUrl, buildIpfsUri } from './utils.js'; @@ -133,7 +132,7 @@ export class StorageService { async pinCertificateMetadata(request: { certificateId: string; - content: Record; + content: unknown; }): Promise { return this.pinJsonNow({ resourceType: 'certificate', @@ -150,7 +149,7 @@ export class StorageService { async pinProjectIdea(request: { projectId: string; - content: Record; + content: unknown; queued?: boolean; }): Promise { if (request.queued) { diff --git a/backend/src/services/storage/worker.ts b/backend/src/services/storage/worker.ts index d8887b24..aa18e7fe 100644 --- a/backend/src/services/storage/worker.ts +++ b/backend/src/services/storage/worker.ts @@ -1,4 +1,3 @@ -// @ts-nocheck import { Job, Worker } from 'bullmq'; import logger from '../../utils/logger.js'; import * as defaultRepository from './asset.repository.js'; diff --git a/backend/src/services/vulnerabilityScanner.service.ts b/backend/src/services/vulnerabilityScanner.service.ts index c8981830..3afa22f6 100644 --- a/backend/src/services/vulnerabilityScanner.service.ts +++ b/backend/src/services/vulnerabilityScanner.service.ts @@ -1,4 +1,3 @@ -// @ts-nocheck /** * Security Vulnerability Scanner — Blockchain Learning Simulator backend. */ @@ -83,7 +82,7 @@ export function scanContractSource(sourceCode: string): ScanResult { const findings: VulnerabilityFinding[] = []; for (let i = 0; i < lines.length; i++) { - const line = lines[i]; + const line = lines[i]!; for (const rule of SCAN_RULES) { if (rule.pattern.test(line)) { findings.push({ diff --git a/backend/src/services/webhooks/queue.ts b/backend/src/services/webhooks/queue.ts index 3464e0f1..16d03f14 100644 --- a/backend/src/services/webhooks/queue.ts +++ b/backend/src/services/webhooks/queue.ts @@ -41,7 +41,6 @@ const createQueue = (name: string, defaultJobOptions?: JobsOptions) => { const redisUrl = new URL(process.env.REDIS_URL || (() => { throw new Error('REDIS_URL environment variable is required'); })()); - const redisUrl = new URL(process.env.REDIS_URL || 'redis://localhost:6379'); return new Queue(name, { connection: { diff --git a/backend/src/simulator/voting.controller.ts b/backend/src/simulator/voting.controller.ts index 95a8b316..76be7e69 100644 --- a/backend/src/simulator/voting.controller.ts +++ b/backend/src/simulator/voting.controller.ts @@ -1,4 +1,3 @@ -// @ts-nocheck import { Request, Response } from 'express'; import { VotingService } from './voting.service.js'; diff --git a/backend/src/utils/audit.ts b/backend/src/utils/audit.ts index 3f6556c6..bcefd385 100644 --- a/backend/src/utils/audit.ts +++ b/backend/src/utils/audit.ts @@ -1,4 +1,3 @@ -// @ts-nocheck import { Request } from 'express'; import { createHash } from 'crypto'; import prisma from '../db/index.js'; diff --git a/contracts/did_registry/Cargo.toml b/contracts/did_registry/Cargo.toml index 6d126b59..e5cf18d4 100644 --- a/contracts/did_registry/Cargo.toml +++ b/contracts/did_registry/Cargo.toml @@ -4,7 +4,6 @@ version = "0.0.0" edition = "2021" [lib] -path = "lib.rs" crate-type = ["cdylib", "rlib"] [dependencies] diff --git a/contracts/did_registry/lib.rs b/contracts/did_registry/src/lib.rs similarity index 100% rename from contracts/did_registry/lib.rs rename to contracts/did_registry/src/lib.rs diff --git a/contracts/multisig_wallet_timelock/Cargo.toml b/contracts/multisig_wallet_timelock/Cargo.toml index 8beefe88..74e0cde7 100644 --- a/contracts/multisig_wallet_timelock/Cargo.toml +++ b/contracts/multisig_wallet_timelock/Cargo.toml @@ -4,7 +4,6 @@ version = "0.1.0" edition = "2021" [lib] -path = "lib.rs" crate-type = ["cdylib", "rlib"] [dependencies] diff --git a/contracts/multisig_wallet_timelock/lib.rs b/contracts/multisig_wallet_timelock/src/lib.rs similarity index 100% rename from contracts/multisig_wallet_timelock/lib.rs rename to contracts/multisig_wallet_timelock/src/lib.rs diff --git a/frontend/.gitignore b/frontend/.gitignore index 5ef6a520..20829442 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -12,6 +12,8 @@ # testing /coverage +**/__snapshots__/ +*.snap # next.js /.next/