Skip to content

Commit 4e84871

Browse files
committed
update
1 parent b165256 commit 4e84871

9 files changed

Lines changed: 71 additions & 35 deletions

File tree

backend/.eslintrc.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,13 @@
1212
"plugin:@typescript-eslint/recommended"
1313
],
1414
"rules": {
15-
"no-console": "error"
15+
"no-console": "error",
16+
"@typescript-eslint/no-unused-vars": [
17+
"error",
18+
{ "argsIgnorePattern": "^_" }
19+
]
1620
},
21+
"ignorePatterns": ["src/**/*.test.ts", "src/**/__tests__/**", "src/services/campaignController.ts"],
1722
"overrides": [
1823
{
1924
"files": ["**/*.test.ts", "tests/**/*.ts"],

backend/package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@
1313
"zod": "^4.3.6"
1414
},
1515
"scripts": {
16-
"lint": "eslint 'src/**/*.ts'"
16+
"build": "tsc -p tsconfig.json",
17+
"dev": "ts-node-dev --respawn src/index.ts",
18+
"test": "vitest run",
19+
"test:coverage": "vitest run --coverage",
20+
"lint": "eslint src/**/*.ts"
1721
},
1822
"devDependencies": {
1923
"@types/better-sqlite3": "^7.6.13",

backend/src/config.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import "dotenv/config";
22
import { normalizeLogLevel } from "./logger";
33

4-
const DEFAULT_NETWORK_PASSPHRASE = "Test SDF Network ; September 2015";
5-
64
const parseOrigins = (originsStr: string): string[] => {
75
return originsStr
86
.split(",")

backend/src/index.ts

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ import {
2626
listCampaignPledges,
2727
listCampaigns,
2828
type ListCampaignsOptions,
29-
softDeleteCampaign,
3029
reconcileOnChainPledge,
3130
refundContributor,
3231
} from "./services/campaignStore";
@@ -35,7 +34,7 @@ import { getCampaignHistory } from "./services/eventHistory";
3534
import { startEventIndexer } from "./services/eventIndexer";
3635
import { fetchOpenIssues } from "./services/openIssues";
3736
import { ensureSorobanRefundConfig, verifyRefundTransaction } from "./services/sorobanRpc";
38-
import { AppError, ApiErrorResponse } from "./types/errors";
37+
import { AppError, ApiErrorDetail, ApiErrorResponse } from "./types/errors";
3938
import {
4039
campaignIdSchema,
4140
claimCampaignPayloadSchema,
@@ -509,40 +508,52 @@ app.get("/api/stats", (_req: Request, res: Response) => {
509508
res.json({ data: stats });
510509
});
511510

512-
app.use((err: any, req: Request, res: Response, _next: express.NextFunction) => {
513-
if (err.message === "Not allowed by CORS") {
511+
app.use((error: unknown, req: RequestWithId, res: Response, _next: express.NextFunction) => {
512+
if (error instanceof Error && error.message === "Not allowed by CORS") {
514513
return res.status(403).json({
515514
success: false,
516515
error: {
517516
code: "FORBIDDEN",
518517
message: "CORS policy violation",
519-
requestId: (req as any).requestId,
518+
requestId: req.requestId,
520519
},
521520
});
522521
}
523522

524-
const statusCode = err instanceof AppError ? err.statusCode : (err.statusCode ?? 500);
525-
const code = err instanceof AppError ? err.code : (err.code ?? "INTERNAL_SERVER_ERROR");
523+
const err =
524+
error instanceof AppError
525+
? error
526+
: error instanceof Error
527+
? error
528+
: new Error("An unexpected error occurred");
529+
530+
const statusCode = err instanceof AppError ? err.statusCode : (err as { statusCode?: number }).statusCode ?? 500;
531+
const code = err instanceof AppError ? err.code : (err as { code?: string }).code ?? "INTERNAL_SERVER_ERROR";
526532
const response: ApiErrorResponse = {
527533
success: false,
528534
error: {
529535
code,
530536
message: err.message || "An unexpected error occurred",
531-
requestId: (req as RequestWithId).requestId,
537+
requestId: req.requestId,
532538
},
533539
};
534540

535-
if (err instanceof AppError && err.details) {
536-
response.error.details = err.details;
537-
} else if (err.details) {
538-
response.error.details = err.details;
541+
const details =
542+
err instanceof AppError
543+
? err.details
544+
: error && typeof error === "object" && "details" in error && Array.isArray((error as { details?: unknown }).details)
545+
? (error as { details?: ApiErrorDetail[] }).details
546+
: undefined;
547+
548+
if (details) {
549+
response.error.details = details;
539550
}
540551

541552
logError(
542553
err,
543554
{
544555
event: "request_error",
545-
requestId: (req as RequestWithId).requestId,
556+
requestId: req.requestId,
546557
method: req.method,
547558
path: req.originalUrl || req.path,
548559
status: statusCode,

backend/src/services/campaignStore.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,7 @@ export function listCampaigns(
353353
const offset = (page - 1) * limit;
354354

355355
const whereClauses: string[] = [];
356-
const params: any[] = [];
356+
const params: Array<unknown> = [];
357357

358358
if (options?.searchQuery && options.searchQuery.trim()) {
359359
const searchTerm = `%${options.searchQuery.trim().toLowerCase()}%`;
@@ -590,6 +590,7 @@ export function createCampaign(input: CampaignInput): CampaignRecord {
590590
title: input.title.trim(),
591591
description: input.description.trim(),
592592
acceptedTokens,
593+
assetCode: acceptedTokens[0] || "",
593594
targetAmount: round(input.targetAmount),
594595
pledgedAmount: 0,
595596
deadline: input.deadline,

backend/src/services/db.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import Database from "better-sqlite3";
2+
import fs from "fs";
23
import path from "path";
34

45
type SQLiteDatabase = ReturnType<typeof Database>;
@@ -24,7 +25,6 @@ export function initDb(): void {
2425
return;
2526
}
2627

27-
const fs = require("fs") as typeof import("fs");
2828
const dbPath = resolveDbPath();
2929
const dir = path.dirname(dbPath);
3030

backend/src/services/eventIndexer.ts

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,19 @@ import { logError, logInfo } from "../logger";
77

88
dotenv.config();
99

10+
type SorobanEvent = {
11+
txHash?: string;
12+
ledger?: number;
13+
event_index?: number;
14+
type?: string;
15+
contract_id?: string;
16+
value?: unknown;
17+
topic?: unknown;
18+
timestamp?: number;
19+
ledgerCloseTime?: number;
20+
[key: string]: unknown;
21+
};
22+
1023
const SOROBAN_RPC_URL = process.env.SOROBAN_RPC_URL || "https://soroban-testnet.stellar.org:443";
1124
const CONTRACT_ID = process.env.CONTRACT_ID || "";
1225

@@ -47,7 +60,7 @@ async function fetchSorobanEvents() {
4760
}
4861

4962

50-
function isDuplicateEvent(db: any, event: any): boolean {
63+
function isDuplicateEvent(db: ReturnType<typeof getDb>, event: SorobanEvent): boolean {
5164
// Use transaction hash for better deduplication if available
5265
if (event.txHash) {
5366
const row = db.prepare(
@@ -67,16 +80,16 @@ function isDuplicateEvent(db: any, event: any): boolean {
6780
}
6881

6982

70-
function parseSorobanEvent(event: any) {
83+
function parseSorobanEvent(event: SorobanEvent) {
7184
// Example event: { ledger, event_index, type, contract_id, txHash, ledgerCloseTime, ...data }
7285
// Map to local event schema
7386
if (!event || !event.type || !event.contract_id) return null;
7487
// Only process contract events
7588
if (event.type !== "contract" || event.contract_id !== CONTRACT_ID) return null;
7689

7790
// Parse event topic and data
78-
const topic = event.topic && Array.isArray(event.topic) ? event.topic.map((t: any) => t.toString()).join(":") : "";
79-
let eventType: any = undefined;
91+
const topic = Array.isArray(event.topic) ? event.topic.map((t) => String(t)).join(":") : "";
92+
let eventType: "created" | "pledged" | "claimed" | "refunded" | undefined;
8093
if (topic.includes("Goal:Create")) eventType = "created";
8194
else if (topic.includes("Goal:Pledge")) eventType = "pledged";
8295
else if (topic.includes("Goal:Claim")) eventType = "claimed";
@@ -85,19 +98,22 @@ function parseSorobanEvent(event: any) {
8598

8699
// Extract campaignId, actor, amount, etc. from event.value or event.data
87100
let campaignId = "";
88-
let actor = undefined;
89-
let amount = undefined;
90-
let metadata = { ...event };
101+
let actor: string | undefined;
102+
let amount: number | undefined;
103+
let metadata: Record<string, unknown> = { ...event };
104+
const value = event.value as Record<string, unknown> | undefined;
91105

92106
try {
93-
if (event.value) {
94-
if (event.value.campaign_id !== undefined) campaignId = String(event.value.campaign_id);
95-
if (event.value.creator) actor = event.value.creator;
96-
if (event.value.contributor) actor = event.value.contributor;
97-
if (event.value.amount !== undefined) amount = Number(event.value.amount);
98-
metadata = { ...event, ...event.value };
107+
if (value) {
108+
if (value.campaign_id !== undefined) campaignId = String(value.campaign_id);
109+
if (value.creator) actor = String(value.creator);
110+
if (value.contributor) actor = String(value.contributor);
111+
if (value.amount !== undefined) amount = Number(value.amount);
112+
metadata = { ...event, ...value };
99113
}
100-
} catch {}
114+
} catch (err: unknown) {
115+
logError(err, { event: "soroban_event_parse_error" }, config.logLevel);
116+
}
101117

102118
// Create blockchain metadata
103119
const blockchainMetadata: BlockchainMetadata = {

backend/tsconfig.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"compilerOptions": {
33
"target": "ES2020",
4-
"module": "node16",
5-
"moduleResolution": "node16",
4+
"module": "es2022",
5+
"moduleResolution": "node",
66
"lib": ["ES2020"],
77
"outDir": "./dist",
88
"rootDir": "./src",

backend/vitest.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export default defineConfig({
3232
provider: "v8",
3333
reporter: ["text", "json", "html"],
3434
exclude: ["node_modules/", "tests/", "dist/"],
35+
reportsDirectory: "coverage",
3536
},
3637
},
3738
});

0 commit comments

Comments
 (0)