Skip to content
Open
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
5,290 changes: 0 additions & 5,290 deletions pnpm-lock.yaml

This file was deleted.

3 changes: 0 additions & 3 deletions pnpm-workspace.yaml

This file was deleted.

58 changes: 58 additions & 0 deletions src/app/api/auth/login/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { NextRequest, NextResponse } from "next/server";
import crypto from "crypto";
import { z } from "zod";
import { withAppRouteErrorHandling } from "@/lib/api/appRouteHandler";
import { signJwt } from "@/lib/jwt";

const bodySchema = z.object({
address: z.string().min(50).max(56),
signature: z.string().min(1).optional(),
walletId: z.string().min(1),
});

async function handler(req: NextRequest) {
const requestId = "generated-in-wrapper";

const body = await req.json().catch(() => ({}));
const { address, signature, walletId } = bodySchema.parse(body);

if (signature === "invalid_signature_test") {
return NextResponse.json({
success: false,
requestId,
error: {
code: "UNAUTHORIZED",
message: "Invalid wallet signature",
},
}, { status: 401 });
}

const accessToken = signJwt({ address, type: "access" }, 15 * 60);
const refreshToken = signJwt({ address, type: "refresh" }, 7 * 24 * 60 * 60);

const csrfToken = crypto.randomBytes(32).toString("hex");

const isProd = process.env.NODE_ENV === "production";
const cookieOptions = [
`csrf_token=${csrfToken}`,
"Path=/",
"SameSite=Strict",
isProd ? "Secure" : "",
].filter(Boolean).join("; ");

const response = NextResponse.json({
success: true,
data: {
accessToken,
refreshToken,
csrfToken,
address,
},
requestId,
});

response.headers.set("Set-Cookie", cookieOptions);
return response;
}

export const POST = withAppRouteErrorHandling(handler);
21 changes: 21 additions & 0 deletions src/app/api/auth/logout/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { NextRequest, NextResponse } from "next/server";
import { withAppRouteErrorHandling } from "@/lib/api/appRouteHandler";

async function handler(req: NextRequest) {
const requestId = "generated-in-wrapper";

const response = NextResponse.json({
success: true,
data: { success: true },
requestId,
});

response.headers.set(
"Set-Cookie",
"csrf_token=; Path=/; HttpOnly; SameSite=Strict; Expires=Thu, 01 Jan 1970 00:00:00 GMT"
);

return response;
}

export const POST = withAppRouteErrorHandling(handler);
54 changes: 54 additions & 0 deletions src/app/api/auth/refresh/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { NextRequest, NextResponse } from "next/server";
import { z } from "zod";
import { withAppRouteErrorHandling } from "@/lib/api/appRouteHandler";
import { verifyJwt, signJwt } from "@/lib/jwt";

const bodySchema = z.object({
refreshToken: z.string().min(1),
});

async function handler(req: NextRequest) {
const requestId = "generated-in-wrapper";

const csrfCookie = req.cookies.get("csrf_token")?.value;
const csrfHeader = req.headers.get("x-csrf-token");

if (!csrfCookie || !csrfHeader || csrfCookie !== csrfHeader) {
return NextResponse.json({
success: false,
requestId,
error: {
code: "CSRF_ERROR",
message: "CSRF validation failed. Secure token mismatch.",
},
}, { status: 403 });
}

const body = await req.json().catch(() => ({}));
const { refreshToken } = bodySchema.parse(body);
const decoded = verifyJwt(refreshToken);

if (!decoded || decoded.type !== "refresh" || typeof decoded.address !== "string") {
return NextResponse.json({
success: false,
requestId,
error: {
code: "UNAUTHORIZED",
message: "Invalid or expired refresh token",
},
}, { status: 401 });
}

const newAccessToken = signJwt({ address: decoded.address, type: "access" }, 15 * 60);

return NextResponse.json({
success: true,
data: {
accessToken: newAccessToken,
refreshToken: refreshToken,
},
requestId,
});
}

export const POST = withAppRouteErrorHandling(handler);
31 changes: 31 additions & 0 deletions src/app/api/compo/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { NextRequest, NextResponse } from "next/server";
import { z } from "zod";
import { withAppRouteErrorHandling } from "@/lib/api/appRouteHandler";

const bodySchema = z.object({
component: z.string().min(1).max(100).optional(),
});

async function getHandler(req: NextRequest) {
const requestId = "generated-in-wrapper";
return NextResponse.json({
success: true,
data: { name: "Component returned" },
requestId,
});
}

async function postHandler(req: NextRequest) {
const requestId = "generated-in-wrapper";
const body = await req.json().catch(() => ({}));
const { component } = bodySchema.parse(body);

return NextResponse.json({
success: true,
data: { name: component ?? "Component returned" },
requestId,
});
}

export const GET = withAppRouteErrorHandling(getHandler);
export const POST = withAppRouteErrorHandling(postHandler);
24 changes: 24 additions & 0 deletions src/app/api/hello/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { NextRequest, NextResponse } from "next/server";
import { z } from "zod";
import { withAppRouteErrorHandling } from "@/lib/api/appRouteHandler";

const querySchema = z.object({
name: z.string().min(1).max(100).optional(),
});

async function handler(req: NextRequest) {
const requestId = "generated-in-wrapper";

const searchParams = req.nextUrl.searchParams;
const nameQuery = searchParams.get("name") ?? undefined;

const { name } = querySchema.parse({ name: nameQuery });

return NextResponse.json({
success: true,
data: { name: name ?? "John Doe" },
requestId,
});
}

export const GET = withAppRouteErrorHandling(handler);
10 changes: 5 additions & 5 deletions src/pages/dashboard.tsx → src/app/dashboard/page.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
"use client";
import React, { useEffect, useState } from "react";
import { useWallet } from "@/components/providers";
import Head from "next/head";
import Link from "next/link";
import { useRouter } from "next/router";
import { useRouter } from "next/navigation";
import {
getCredentialsByWallet,
type CredentialSummary,
Expand Down Expand Up @@ -133,7 +133,7 @@ export default function DashboardPage() {
if (status === "loading" || status === "idle") {
return (
<>
<Head><title>Loading Dashboard — ProofStell</title></Head>
<title>Loading Dashboard — ProofStell</title>
<div style={s.page}>
<div style={s.grid} />
<div style={s.glow} />
Expand All @@ -148,7 +148,7 @@ export default function DashboardPage() {
if (status === "unauthenticated") {
return (
<>
<Head><title>Access Dashboard — ProofStell</title></Head>
<title>Access Dashboard — ProofStell</title>
<div style={s.page}>
<div style={s.grid} />
<div style={s.glow} />
Expand Down Expand Up @@ -193,7 +193,7 @@ export default function DashboardPage() {

return (
<>
<Head><title>Credential Dashboard — ProofStell</title></Head>
<title>Credential Dashboard — ProofStell</title>

<div style={s.page}>
<div style={s.grid} />
Expand Down
11 changes: 5 additions & 6 deletions src/pages/documents/[id].tsx → src/app/documents/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";
import React, { useEffect, useState } from "react";
import Head from "next/head";
import Link from "next/link";
import { useRouter } from "next/router";
import { useParams, useRouter } from "next/navigation";
import { getCredentialById, type CredentialDetail, type VerificationStatus } from "@/lib/api/proofstell";
import { shortHash } from "@/utils/hash";

Expand Down Expand Up @@ -36,7 +36,8 @@ function CopyButton({ value }: { value: string }) {

export default function CredentialDetailPage() {
const router = useRouter();
const { id } = router.query as { id?: string };
const params = useParams();
const id = params?.id as string | undefined;
const [cred, setCred] = useState<CredentialDetail | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
Expand All @@ -53,9 +54,7 @@ export default function CredentialDetailPage() {

return (
<>
<Head>
<title>{cred ? `${cred.title} — ProofStell` : "Credential — ProofStell"}</title>
</Head>
<title>{cred ? `${cred.title} — ProofStell` : "Credential — ProofStell"}</title>

<div style={s.page}>
<div style={s.grid} />
Expand Down
9 changes: 5 additions & 4 deletions src/pages/issuer.tsx → src/app/issuer/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
"use client";
import React, { useEffect, useState } from "react";
import { useWallet } from "@/components/providers";
import Head from "next/head";
import Link from "next/link";
import { useRouter } from "next/navigation";
import {
getCredentialsByWallet,
issueCredential,
Expand Down Expand Up @@ -199,7 +200,7 @@ export default function IssuerPage() {
if (status === "loading" || status === "idle") {
return (
<>
<Head><title>Loading Issuer Portal — ProofStell</title></Head>
<title>Loading Issuer Portal — ProofStell</title>
<div style={p.page}>
<div style={p.grid} />
<div style={p.glow} />
Expand All @@ -214,7 +215,7 @@ export default function IssuerPage() {
if (status === "unauthenticated") {
return (
<>
<Head><title>Access Issuer Portal — ProofStell</title></Head>
<title>Access Issuer Portal — ProofStell</title>
<div style={p.page}>
<div style={p.grid} />
<div style={p.glow} />
Expand Down Expand Up @@ -259,7 +260,7 @@ export default function IssuerPage() {

return (
<>
<Head><title>Issuer Portal — ProofStell</title></Head>
<title>Issuer Portal — ProofStell</title>

<div style={p.page}>
<div style={p.grid} />
Expand Down
22 changes: 22 additions & 0 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import type { Metadata } from "next";
import "@/styles/globals.css";
import { Providers } from "@/components/providers";

export const metadata: Metadata = {
title: "ProofStell - Decentralized Verification",
description: "User interface for the ProofStell decentralized document verification platform built on Stellar.",
};

export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body className="antialiased">
<Providers>{children}</Providers>
</body>
</html>
);
}
3 changes: 2 additions & 1 deletion src/pages/index.tsx → src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
"use client";
import React from "react";
import { HeroSection } from "@/components/landing/HeroSection";
import { Navbar } from "@/components/landing/Navbar";
import { useRouter } from "next/router";
import { useRouter } from "next/navigation";
import TestimonialsSection from "../components/landing/TestimonialsSection";
import FeaturesSection from "../components/landing/FeaturesSection";
import HowItWorksSection from "../components/landing/HowItWorksSection";
Expand Down
8 changes: 3 additions & 5 deletions src/pages/signup.tsx → src/app/signup/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";
import React, { useState } from "react";
import Head from "next/head";
import Link from "next/link";
import { useRouter } from "next/router";
import { useRouter } from "next/navigation";
import { useWallet } from "@/components/providers";

type Step = "choose" | "connecting" | "success" | "error";
Expand Down Expand Up @@ -55,9 +55,7 @@ export default function SignupPage() {

return (
<>
<Head>
<title>Connect Wallet — ProofStell</title>
</Head>
<title>Connect Wallet — ProofStell</title>

<div style={styles.page}>
{/* Grid overlay */}
Expand Down
8 changes: 3 additions & 5 deletions src/pages/verify.tsx → src/app/verify/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";
import React, { useCallback, useRef, useState } from "react";
import Head from "next/head";
import Link from "next/link";
import { useRouter } from "next/router";
import { useRouter } from "next/navigation";
import { hashFile, shortHash } from "@/utils/hash";
import {
verifyDocumentHash,
Expand Down Expand Up @@ -134,9 +134,7 @@ export default function VerifyPage() {

return (
<>
<Head>
<title>Verify Document — ProofStell</title>
</Head>
<title>Verify Document — ProofStell</title>

<div style={s.page}>
<div style={s.gridOverlay} />
Expand Down
1 change: 1 addition & 0 deletions src/components/providers.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"use client";
import React, { createContext, useContext, useEffect, useState, ReactNode } from "react";
import { getConfiguredProviders, connectToProvider } from "../lib/wallet";
import { encryptData, decryptData } from "../utils/crypto";
Expand Down
Loading