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
2 changes: 2 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/usr/bin/env sh
npx lint-staged --concurrent false
2 changes: 2 additions & 0 deletions .husky/pre-push
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/usr/bin/env sh
npm run build
4 changes: 4 additions & 0 deletions .lintstagedrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"*.{js,jsx,ts,tsx}": ["eslint --fix", "prettier --write"],
"*.{json,md,css}": ["prettier --write"]
}
45 changes: 24 additions & 21 deletions app/api/compliance/status/route.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,31 @@
import { NextRequest, NextResponse } from "next/server";
import { NextResponse } from "next/server";
import { getCurrentUser } from "@/lib/server-auth";
import { ComplianceService } from "@/lib/services/compliance";
import { TermsService } from "@/lib/services/terms";

export async function GET(request: NextRequest) {
try {
const user = await getCurrentUser();
if (!user) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
export async function GET() {
try {
const user = await getCurrentUser();
if (!user) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}

const compliance = await ComplianceService.getUserCompliance(user.id);
const remaining = await ComplianceService.getRemainingLimits(user.id);
const termsStatus = await TermsService.getUserTermsStatus(user.id);
const nextTier = ComplianceService.getNextTier(compliance.currentTier);
const compliance = await ComplianceService.getUserCompliance(user.id);
const remaining = await ComplianceService.getRemainingLimits(user.id);
const termsStatus = await TermsService.getUserTermsStatus(user.id);
const nextTier = ComplianceService.getNextTier(compliance.currentTier);

return NextResponse.json({
compliance,
remaining,
termsStatus,
nextTier,
});
} catch (error) {
console.error("Error fetching compliance status:", error);
return NextResponse.json({ error: "Internal Server Error" }, { status: 500 });
}
return NextResponse.json({
compliance,
remaining,
termsStatus,
nextTier,
});
} catch (error) {
console.error("Error fetching compliance status:", error);
return NextResponse.json(
{ error: "Internal Server Error" },
{ status: 500 },
);
}
}
65 changes: 41 additions & 24 deletions app/api/compliance/terms/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,51 @@ import { NextRequest, NextResponse } from "next/server";
import { getCurrentUser } from "@/lib/server-auth";
import { TermsService } from "@/lib/services/terms";

export async function GET(request: NextRequest) {
try {
const terms = await TermsService.getCurrentTermsVersion();
return NextResponse.json(terms);
} catch (error) {
return NextResponse.json({ error: "Failed to fetch terms" }, { status: 500 });
}
export async function GET() {
try {
const terms = await TermsService.getCurrentTermsVersion();
return NextResponse.json(terms);
} catch (error) {
console.error("Failed to fetch terms:", error);
return NextResponse.json(
{ error: "Failed to fetch terms" },
{ status: 500 },
);
}
}

export async function POST(request: NextRequest) {
try {
const user = await getCurrentUser();
if (!user) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}

const { termsVersionId } = await request.json();
const ip = request.headers.get('x-forwarded-for') || '0.0.0.0';
const userAgent = request.headers.get('user-agent') || 'unknown';
try {
const user = await getCurrentUser();
if (!user) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}

const acceptance = await TermsService.acceptTerms(user.id, termsVersionId, {
ipAddress: ip,
userAgent,
});
const { termsVersionId } = await request.json();

return NextResponse.json(acceptance);
} catch (error) {
console.error("Error accepting terms:", error);
return NextResponse.json({ error: "Failed to accept terms" }, { status: 500 });
// Validate termsVersionId
if (!termsVersionId || typeof termsVersionId !== "string") {
return NextResponse.json(
{ error: "Invalid or missing termsVersionId" },
{ status: 400 },
);
}

const forwardedFor = request.headers.get("x-forwarded-for");
const ip = forwardedFor ? forwardedFor.split(",")[0].trim() : "0.0.0.0";
const userAgent = request.headers.get("user-agent") || "unknown";

const acceptance = await TermsService.acceptTerms(user.id, termsVersionId, {
ipAddress: ip,
userAgent,
});

return NextResponse.json(acceptance);
} catch (error) {
console.error("Error accepting terms:", error);
return NextResponse.json(
{ error: "Failed to accept terms" },
{ status: 500 },
);
}
}
88 changes: 57 additions & 31 deletions app/api/compliance/upgrade/route.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,67 @@
import { NextRequest, NextResponse } from "next/server";
import { getCurrentUser } from "@/lib/server-auth";
import { VerificationService } from "@/lib/services/verification";
import { ComplianceService } from "@/lib/services/compliance";
import { KYCTier } from "@/types/compliance";

export async function POST(request: NextRequest) {
try {
const user = await getCurrentUser();
if (!user) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}

const { targetTier } = await request.json();

const verificationRequest = await VerificationService.createVerificationRequest(
user.id,
targetTier
);

return NextResponse.json(verificationRequest);
} catch (error: any) {
console.error("Error creating verification request:", error);
return NextResponse.json(
{ error: error.message || "Failed to create verification request" },
{ status: 400 }
);
try {
const user = await getCurrentUser();
if (!user) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}

const { targetTier } = await request.json();

const VALID_TIERS = Object.keys(ComplianceService.TIER_CONFIGS).filter(
(t) => t !== "UNVERIFIED",
) as KYCTier[];

// Validate targetTier
if (!targetTier || !VALID_TIERS.includes(targetTier as KYCTier)) {
return NextResponse.json(
{ error: "Invalid or missing targetTier" },
{ status: 400 },
);
}

const verificationRequest =
await VerificationService.createVerificationRequest(user.id, targetTier);

return NextResponse.json(verificationRequest);
} catch (error) {
console.error("Error creating verification request:", error);

const isValidationError =
error instanceof Error &&
(error.name === "ValidationError" ||
error.message.includes("Invalid") ||
error.message.includes("not allowed"));

return NextResponse.json(
{
error:
(error as Error).message || "Failed to create verification request",
},
{ status: isValidationError ? 400 : 500 },
);
}
}

export async function GET(request: NextRequest) {
try {
const user = await getCurrentUser();
if (!user) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}

const status = await VerificationService.getVerificationStatus(user.id);
return NextResponse.json(status);
} catch (error) {
return NextResponse.json({ error: "Failed to fetch verification status" }, { status: 500 });
export async function GET() {
try {
const user = await getCurrentUser();
if (!user) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}

const status = await VerificationService.getVerificationStatus(user.id);
return NextResponse.json(status);
} catch (error) {
console.error("Failed to fetch verification status:", error);
return NextResponse.json(
{ error: "Failed to fetch status" },
{ status: 500 },
);
}
}
78 changes: 54 additions & 24 deletions app/api/withdrawal/submit/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,59 @@ import { getCurrentUser } from "@/lib/server-auth";
import { WithdrawalService } from "@/lib/services/withdrawal";

export async function POST(request: NextRequest) {
try {
const user = await getCurrentUser();
if (!user) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}

const { amount, currency, destinationId } = await request.json();
const ip = request.headers.get('x-forwarded-for') || '0.0.0.0';

const withdrawal = await WithdrawalService.submit(
user.id,
amount,
currency,
destinationId,
ip
);

return NextResponse.json(withdrawal);
} catch (error: any) {
console.error("Error submitting withdrawal:", error);
return NextResponse.json(
{ error: error.message || "Withdrawal failed" },
{ status: 400 }
);
try {
const user = await getCurrentUser();
if (!user) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}

const { amount, currency, destinationId } = await request.json();

// Input Validation
if (typeof amount !== "number" || !isFinite(amount) || amount <= 0) {
return NextResponse.json({ error: "Invalid amount" }, { status: 400 });
}
if (!currency || typeof currency !== "string") {
return NextResponse.json({ error: "Invalid currency" }, { status: 400 });
}
if (!destinationId || typeof destinationId !== "string") {
return NextResponse.json(
{ error: "Invalid destinationId" },
{ status: 400 },
);
}

const forwardedFor = request.headers.get("x-forwarded-for");
const ip = forwardedFor ? forwardedFor.split(",")[0].trim() : "0.0.0.0";

const withdrawal = await WithdrawalService.submit(
user.id,
amount,
currency,
destinationId,
ip,
);

return NextResponse.json(withdrawal);
} catch (error) {
console.error("Error submitting withdrawal:", error);

let message = "Withdrawal failed";
let status = 500; // Default to 500

if (error instanceof Error) {
message = error.message;
const err = error as Error & { status?: number; statusCode?: number };
status =
err.status ||
err.statusCode ||
(error.name === "ValidationError" || error.name === "BadRequestError"
? 400
: 500);
} else {
message = String(error);
}

return NextResponse.json({ error: message }, { status });
}
}
Loading