summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPawel Zelawski <pawel.zelawski@outlook.com>2025-04-10 14:35:56 +0200
committerPawel Zelawski <pawel.zelawski@outlook.com>2025-04-10 14:35:56 +0200
commitd858bd4e0fc7aaba15509a17b35138e843667bc1 (patch)
treeb8251d1579bebe7128efe5b93ed734f9a75b234b
parente29a1b9a652fe254d13afef7f5c7a3d7a64b68cc (diff)
refactor: improve layout and spacing across all views
- Remove extra space at the top of the page by adjusting body and root styles - Add consistent 20px top padding to all views through app-container - Simplify layout structure by removing unnecessary flex containers - Adjust spacing between elements for better visual hierarchy: - Reduce gaps between elements from 1.5rem to 1rem - Reduce bottom margin of h1 from 1.5rem to 1rem - Remove negative margins and extra padding - Maintain consistent box dimensions (390px width, 560px height) for all views - Ensure proper spacing for QR code and other content within views
-rw-r--r--index.html12
-rw-r--r--package.json6
-rw-r--r--src/client/App.tsx101
-rw-r--r--src/client/index.css301
-rw-r--r--src/client/utils.ts33
-rw-r--r--src/server/main.ts62
-rw-r--r--tsconfig.json9
-rw-r--r--vite.config.ts4
8 files changed, 394 insertions, 134 deletions
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..c6057da
--- /dev/null
+++ b/index.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <title>DigiID-TS Demo</title>
+ </head>
+ <body>
+ <div id="root"></div>
+ <script type="module" src="/src/client/main.tsx"></script>
+ </body>
+</html> \ No newline at end of file
diff --git a/package.json b/package.json
index 9bd6c3c..bfd0b66 100644
--- a/package.json
+++ b/package.json
@@ -2,13 +2,13 @@
"name": "digiid-ts-demo",
"version": "1.0.0",
"description": "",
- "main": "index.js",
"type": "module",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev:frontend": "vite",
- "dev:backend": "nodemon --watch src/server --ext ts --exec \"node --loader ts-node/esm src/server/main.ts\"",
- "dev": "npm-run-all --parallel dev:frontend dev:backend",
+ "build:backend:watch": "tsc -p tsconfig.json --watch",
+ "dev:backend": "nodemon dist/server/server/main.js",
+ "dev": "npm-run-all --parallel dev:frontend build:backend:watch dev:backend",
"build": "tsc && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview"
diff --git a/src/client/App.tsx b/src/client/App.tsx
index 00280cd..b3f0308 100644
--- a/src/client/App.tsx
+++ b/src/client/App.tsx
@@ -99,7 +99,7 @@ function App() {
setQrCodeDataUrl(null); // Clear previous QR code if any
setSessionId(null);
setResultData(null);
- console.log('Requesting new DigiID session...');
+ console.log('Requesting new Digi-ID session...');
try {
const response = await fetch('/api/digiid/start');
@@ -113,9 +113,9 @@ function App() {
setQrCodeDataUrl(data.qrCodeDataUrl);
setUiState('waiting'); // Move to waiting state
} catch (err) {
- console.error('Error starting DigiID session:', err);
+ console.error('Error starting Digi-ID session:', err);
const message = err instanceof Error ? err.message : 'An unknown error occurred';
- setError(`Failed to initiate DigiID: ${message}`);
+ setError(`Failed to initiate Digi-ID: ${message}`);
setUiState('initial'); // Stay in initial state on error
} finally {
setIsLoading(false);
@@ -135,57 +135,70 @@ function App() {
return (
<div className="app-container">
- <h1>DigiID-TS Demo</h1>
+ <h1>Digi-ID TypeScript Integration Demo</h1>
- {/* --- Initial State --- */}
+ {/* --- Views based on uiState --- */}
{uiState === 'initial' && (
<div className="initial-view">
- <h2>Welcome</h2>
- <p>Click the button below to generate a DigiID login QR code.</p>
- {/* TODO: Add Icon here */}
- {/* <img src="/assets/YOUR_ICON_FILENAME.png" alt="DigiID Icon" width="100" /> */}
- <button onClick={handleStart} disabled={isLoading}>
- {isLoading ? 'Generating QR...' : 'Start DigiID Login'}
+ <button onClick={handleStart} disabled={isLoading} className="signin-button">
+ <img src="/assets/digiid-logo.png" alt="Digi-ID Logo" width="24" height="24" />
+ <span>{isLoading ? 'Generating QR...' : 'Sign in with Digi-ID'}</span>
</button>
</div>
)}
- {/* --- Waiting State --- */}
- {uiState === 'waiting' && qrCodeDataUrl && (
- <div className="waiting-view">
- <h2>Scan the QR Code</h2>
- <p>Scan the QR code below using your DigiID compatible mobile wallet.</p>
- <img src={qrCodeDataUrl} alt="DigiID QR Code" width="250" />
- <p>Waiting for authentication...</p>
- {/* Optional: Add a cancel button here */}
- <button onClick={handleReset}>Cancel</button>
+ {uiState !== 'initial' && (
+ <div className="view-container">
+ {uiState === 'waiting' && qrCodeDataUrl && (
+ <div className="waiting-view view-box">
+ <h2>Scan the QR Code</h2>
+ <p>Scan the QR code below using your Digi-ID compatible mobile wallet.</p>
+ <img src={qrCodeDataUrl} alt="Digi-ID QR Code" width="250" />
+ <p>Waiting for authentication...</p>
+ <button onClick={handleReset}>Cancel</button>
+ </div>
+ )}
+
+ {uiState === 'success' && resultData?.address && (
+ <div className="success-view view-box">
+ <svg xmlns="http://www.w3.org/2000/svg" className="checkmark-icon" viewBox="0 0 52 52">
+ <circle className="checkmark__circle" cx="26" cy="26" r="25" fill="none"/>
+ <path className="checkmark__check" fill="none" d="M14.1 27.2l7.1 7.2 16.7-16.8"/>
+ </svg>
+ <h2>Authentication Successful!</h2>
+ <p>Verified Address:</p>
+ <p className="address">{resultData.address}</p>
+ <p>Address Format: {getDigiByteAddressType(resultData.address)}</p>
+ <button onClick={handleReset}>Start Over</button>
+ </div>
+ )}
+
+ {uiState === 'failed' && (
+ <div className="failed-view view-box">
+ <h2>Authentication Failed</h2>
+ <p className="error-message">
+ Reason: {resultData?.error || error || 'An unknown error occurred.'}
+ </p>
+ <button onClick={handleReset}>Try Again</button>
+ </div>
+ )}
</div>
)}
- {/* --- Success State --- */}
- {uiState === 'success' && resultData?.address && (
- <div className="success-view">
- <h2>Authentication Successful!</h2>
- <p>Verified Address:</p>
- <p className="address">{resultData.address}</p>
- <p>Address Type: {getDigiByteAddressType(resultData.address)}</p>
- <button onClick={handleReset}>Start Over</button>
- </div>
- )}
-
- {/* --- Failed State --- */}
- {uiState === 'failed' && (
- <div className="failed-view">
- <h2>Authentication Failed</h2>
- <p className="error-message">
- Reason: {resultData?.error || error || 'An unknown error occurred.'}
- </p>
- <button onClick={handleReset}>Try Again</button>
- </div>
- )}
-
- {/* General error display (e.g., for initial start error) */}
- {error && uiState === 'initial' && <p className="error-message">Error: {error}</p>}
+ {/* --- Description section (Always visible below the view container) --- */}
+ <div className="description">
+ <p className="code-link">
+ This application demonstrates integrating Digi-ID authentication using the <a href="https://github.com/pawelzelawski/digiid-ts" target="_blank" rel="noopener noreferrer">digiid-ts</a> library.
+ </p>
+ <p>
+ Upon successful verification, the system can identify the following DigiByte address formats:
+ </p>
+ <ul>
+ <li>Legacy Addresses (P2PKH) - starting with 'D'</li>
+ <li>Pay-to-Script-Hash Addresses (P2SH) - commonly starting with 'S'</li>
+ <li>Segregated Witness (SegWit) Addresses - starting with 'dgb1'</li>
+ </ul>
+ </div>
</div>
);
}
diff --git a/src/client/index.css b/src/client/index.css
index 2349c9b..6cf1cfb 100644
--- a/src/client/index.css
+++ b/src/client/index.css
@@ -2,9 +2,24 @@
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
- color-scheme: light dark;
- color: rgba(255, 255, 255, 0.87);
- background-color: #242424;
+
+ /* DigiID Inspired Colors (Light Theme Defaults) */
+ --digi-blue: #003366;
+ --digi-accent: #007bff;
+ --digi-text-light: #f0f0f0;
+ --digi-text-dark: #222222;
+ --digi-bg-dark: var(--digi-blue);
+ --digi-bg-light: #ffffff;
+ --digi-border-dark: #0059b3;
+ --digi-border-light: #cccccc;
+ --digi-address-bg-light: #e8f0fe;
+ --digi-error-light: #d9534f;
+ --digi-code-bg-light: #e8f0fe;
+
+ /* Default to Light Theme */
+ color-scheme: light;
+ color: var(--digi-text-dark);
+ background-color: var(--digi-bg-light);
font-synthesis: none;
text-rendering: optimizeLegibility;
@@ -14,8 +29,6 @@
body {
margin: 0;
- display: flex;
- place-items: center;
min-width: 320px;
min-height: 100vh;
}
@@ -23,7 +36,7 @@ body {
#root {
max-width: 1280px;
margin: 0 auto;
- padding: 2rem;
+ padding: 0.5rem 2rem;
text-align: center;
}
@@ -31,85 +44,301 @@ body {
display: flex;
flex-direction: column;
align-items: center;
- gap: 1.5rem;
+ gap: 1rem;
+ padding: 20px 0 0;
+ margin-top: 0;
}
-h1 {
- font-size: 2.5em;
- line-height: 1.1;
+.app-container h1 {
+ margin: 0;
margin-bottom: 1rem;
+ color: var(--digi-blue);
+ font-size: 1.8em;
+}
+
+/* Style the container that holds the changing views */
+.view-container {
+ width: 100%; /* Keep width if needed for centering the box */
+ display: flex; /* Keep display flex for centering */
+ justify-content: center; /* Center the box itself */
+}
+
+h1 {
+ font-size: 1.5em;
+ line-height: 1.2;
+ margin: 0;
+ color: var(--digi-blue);
}
button {
border-radius: 8px;
border: 1px solid transparent;
- padding: 0.6em 1.2em;
+ padding: 0.5em 1em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
- background-color: #1a1a1a;
+ background-color: var(--digi-accent);
+ color: var(--digi-text-light);
cursor: pointer;
- transition: border-color 0.25s;
+ transition: border-color 0.25s, background-color 0.25s;
+ margin-top: 0.5rem;
}
button:hover {
- border-color: #646cff;
+ background-color: var(--digi-blue);
+ border-color: var(--digi-blue);
}
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
}
button:disabled {
- background-color: #333;
+ background-color: #555; /* Keep disabled distinct */
cursor: not-allowed;
opacity: 0.6;
}
-.initial-view, .waiting-view, .success-view, .failed-view {
+/* Box styling for views that need it */
+.waiting-view, .success-view, .failed-view {
padding: 1.5rem;
- border: 1px solid #555;
+ border: 1px solid var(--digi-border-light);
+ background-color: rgba(0, 0, 0, 0.02);
border-radius: 8px;
display: flex;
flex-direction: column;
align-items: center;
+ justify-content: center;
gap: 1rem;
- min-width: 300px;
+ width: 390px;
+ height: 560px;
+ box-sizing: border-box;
+}
+
+.waiting-view {
+ padding: 1rem;
+ gap: 0.5rem;
+}
+
+.waiting-view h2 {
+ margin-bottom: 0.1rem;
+}
+
+.waiting-view p {
+ margin: 0.1rem 0;
}
.waiting-view img {
- background-color: white; /* Ensure QR code background is white */
- padding: 10px;
- border-radius: 4px;
+ margin: 0.5rem auto;
+}
+
+.waiting-view button {
+ margin-top: 0.5rem;
}
.address {
font-family: monospace;
- background-color: #333;
+ background-color: var(--digi-address-bg-light);
+ color: var(--digi-text-dark);
padding: 0.5em;
border-radius: 4px;
word-break: break-all;
}
.error-message {
- color: #ff6b6b;
+ color: var(--digi-error-light);
font-weight: bold;
}
+code, .code-link a {
+ background-color: var(--digi-code-bg-light);
+ color: var(--digi-text-dark);
+ padding: 0.2em 0.4em;
+ border-radius: 3px;
+ font-family: monospace;
+ text-decoration: none; /* Remove underline from link part */
+}
+
+.code-link a:hover {
+ text-decoration: underline;
+}
-@media (prefers-color-scheme: light) {
- :root {
- color: #213547;
- background-color: #ffffff;
- }
- button {
- background-color: #f9f9f9;
+.description {
+ font-size: 0.9em;
+ line-height: 1.4;
+ margin: 0;
+ text-align: center;
+ max-width: 100%;
+}
+
+.description ul {
+ margin-top: 0.5em;
+ padding-left: 20px;
+}
+
+.description li {
+ margin-bottom: 0.3em;
+ text-align: left;
+}
+
+/* Base styling ONLY for view transitions */
+.view-box {
+ opacity: 1;
+ transform: scale(1);
+ transition: opacity 0.5s ease-in-out, transform 0.5s ease-in-out;
+}
+
+/* Animation for success */
+.success-view {
+ /* Add specific styles if needed, e.g., border color */
+ border-color: #28a745; /* Green border for success */
+}
+
+/* Checkmark Icon Styles & Animation */
+.checkmark-icon {
+ width: 80px;
+ height: 80px;
+ border-radius: 50%;
+ display: block;
+ stroke-width: 3;
+ stroke: #28a745; /* Green checkmark */
+ stroke-miterlimit: 10;
+ margin: 10px auto;
+ box-shadow: inset 0px 0px 0px #ffffff; /* Start transparent */
+ animation: scale .3s ease-in-out .9s both, fill .4s ease-in-out .9s both;
+}
+
+.checkmark__circle {
+ stroke-dasharray: 166;
+ stroke-dashoffset: 166;
+ stroke-width: 2;
+ stroke-miterlimit: 10;
+ stroke: #7ac142;
+ fill: none;
+ animation: stroke 0.6s cubic-bezier(0.65, 0, 0.45, 1) forwards;
+}
+
+.checkmark__check {
+ transform-origin: 50% 50%;
+ stroke-dasharray: 48;
+ stroke-dashoffset: 48;
+ animation: stroke 0.3s cubic-bezier(0.65, 0, 0.45, 1) 0.8s forwards;
+}
+
+@keyframes stroke {
+ 100% {
+ stroke-dashoffset: 0;
}
- .app-container {
- /* Add light mode adjustments if needed */
+}
+
+@keyframes scale {
+ 0%, 100% {
+ transform: none;
}
- .initial-view, .waiting-view, .success-view, .failed-view {
- border-color: #ccc;
+ 50% {
+ transform: scale3d(1.1, 1.1, 1);
}
- .address {
- background-color: #eee;
+}
+
+@keyframes fill {
+ 100% {
+ box-shadow: inset 0px 0px 0px 40px #7ac142; /* Fill effect */
}
+}
+
+/* Adjust QR code size to fit better in the box */
+.qr-code {
+ width: 240px;
+ height: 240px;
+ margin: 0 auto;
+}
+
+/* Ensure success animation fits */
+.success-animation {
+ width: 100px;
+ height: 100px;
+ margin: 0 auto;
+}
+
+/* Ensure waiting spinner fits */
+.waiting-spinner {
+ width: 40px;
+ height: 40px;
+ margin: 0 auto;
+}
+
+/* Ensure failed X mark fits */
+.failed-x {
+ width: 60px;
+ height: 60px;
+ margin: 0 auto;
+}
+
+/* Remove dark mode specific overrides */
+/* @media (prefers-color-scheme: dark) { ... } */
+
+/* Initial view without box */
+.initial-view {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ min-height: 120px;
+}
+
+.initial-view h2 {
+ margin: 0;
+ color: var(--digi-blue);
+ font-size: 1.5em;
+}
+
+.initial-view .description {
+ margin-top: 0.1rem;
+ margin-bottom: 0.5rem;
+}
+
+.initial-view .qr-code {
+ margin: 0.5rem auto;
+}
+
+.initial-view .waiting-text {
+ margin: 0.25rem 0;
+}
+
+.initial-view button {
+ margin-top: 0.25rem;
+}
+
+.signin-button {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 0.5rem;
+ padding: 0.6rem 1.2rem;
+ background-color: white;
+ color: #1a237e;
+ border: 1px solid var(--digi-border-light);
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
+ transition: all 0.2s ease;
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
+}
+
+.signin-button:hover {
+ background-color: #f8f9fa;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+}
+
+.signin-button:disabled {
+ opacity: 0.7;
+ cursor: not-allowed;
+}
+
+.signin-button img {
+ margin: 0;
+ width: 20px;
+ height: 20px;
+}
+
+.signin-button span {
+ font-weight: 600;
+ font-size: 0.95rem;
+ letter-spacing: 0.3px;
} \ No newline at end of file
diff --git a/src/client/utils.ts b/src/client/utils.ts
index cd9c8ba..6a95f7b 100644
--- a/src/client/utils.ts
+++ b/src/client/utils.ts
@@ -1,20 +1,31 @@
-// Simple utility to determine DigiByte address type based on prefix
+// Utility to determine DigiByte address type based on prefix
-export type DigiByteAddressType = 'DigiByte (DGB)' | 'DigiAsset (DGA)' | 'Unknown';
+export type DigiByteAddressFormat = 'Legacy (P2PKH)' | 'Script (P2SH)' | 'SegWit (Bech32)' | 'Unknown';
-export function getDigiByteAddressType(address: string | undefined | null): DigiByteAddressType {
+/**
+ * Determines the format of a DigiByte address based on its prefix.
+ * Note: P2SH addresses on DigiByte often start with 'S', but complex scripts
+ * might result in different prefixes. This function covers common cases.
+ * Legacy starts with 'D'. SegWit starts with 'dgb1'.
+ *
+ * @param address The DigiByte address string.
+ * @returns The determined address format.
+ */
+export function getDigiByteAddressType(address: string | undefined | null): DigiByteAddressFormat {
if (!address) {
return 'Unknown';
}
if (address.startsWith('dgb1')) {
- return 'DigiByte (DGB)';
+ return 'SegWit (Bech32)';
}
- // Add other prefixes if DigiAssets use a distinct one, e.g., 'dga1'
- // For now, assume non-DGB is DigiAsset, but this might need refinement
- // depending on actual DigiAsset address formats.
- else {
- // Assuming DigiAssets might start differently or be the fallback
- // This is a placeholder assumption.
- return 'DigiAsset (DGA)'; // Placeholder - ADJUST BASED ON ACTUAL DGA PREFIX
+ if (address.startsWith('S')) { // Common prefix for P2SH on DGB
+ return 'Script (P2SH)';
}
+ if (address.startsWith('D')) {
+ return 'Legacy (P2PKH)';
+ }
+
+ // If it doesn't match known prefixes, return Unknown
+ // Could potentially add DigiAsset checks here if they have distinct prefixes
+ return 'Unknown';
} \ No newline at end of file
diff --git a/src/server/main.ts b/src/server/main.ts
index f9e43d5..a62e19e 100644
--- a/src/server/main.ts
+++ b/src/server/main.ts
@@ -2,8 +2,7 @@ import dotenv from 'dotenv';
import express, { Request, Response } from 'express';
import { randomBytes } from 'crypto';
import qrcode from 'qrcode';
-// Import actual functions from the linked library
-import { generateDigiIDUri, verifyDigiIDCallback, DigiIDError } from 'digiid-ts';
+import { generateDigiIDUri, verifyDigiIDCallback } from 'digiid-ts';
// Load environment variables from .env file
dotenv.config();
@@ -50,6 +49,7 @@ app.get('/api/digiid/start', async (req: Request, res: Response) => {
}
const unsecure = callbackUrl.startsWith('http://');
+ // Use the actual function from digiid-ts
const digiIdUri = generateDigiIDUri({ callbackUrl, unsecure, nonce });
console.log(`Generated DigiID URI: ${digiIdUri} for session ${sessionId}`);
@@ -62,11 +62,13 @@ app.get('/api/digiid/start', async (req: Request, res: Response) => {
} catch (error) {
console.error('Error in /api/digiid/start:', error);
- // Check if it's a DigiIDError specifically from generateDigiIDUri
- if (error instanceof DigiIDError) {
- res.status(400).json({ error: `Failed to generate URI: ${error.message}` });
- } else {
- res.status(500).json({ error: 'Internal server error during start' });
+ // Ensure response is sent even on error
+ if (!res.headersSent) { // Check if response hasn't already been sent
+ if (error instanceof Error) {
+ res.status(400).json({ error: `Failed to generate URI: ${error.message}` });
+ } else {
+ res.status(500).json({ error: 'Internal server error during start' });
+ }
}
}
});
@@ -112,46 +114,39 @@ app.post('/api/digiid/callback', async (req: Request, res: Response) => {
// Retrieve the session *before* the try/finally block for verification
const session = sessionStore.get(sessionId);
if (!session) {
- // This case should be rare if nonceToSessionMap is consistent
console.error(`Critical: Session data missing for ${sessionId} despite nonce match.`);
- return res.status(500).send('Internal server error: Session data missing.');
+ if (!res.headersSent) res.status(500).send('Internal server error: Session data missing.');
+ return; // Explicitly return void
}
if (session.status !== 'pending') {
console.warn('Session already processed:', sessionId, session.status);
- // Treat as success here, client will get final status via polling
- return res.status(200).send('Session already processed.');
+ if (!res.headersSent) res.status(200).send('Session already processed.');
+ return; // Explicitly return void
}
-
// --- Verification ---
let expectedCallbackUrl: string;
try {
const publicUrl = process.env.PUBLIC_URL;
- if (!publicUrl) {
- // Throw specific error to be caught below
- throw new Error('PUBLIC_URL environment variable is not configured on the server.');
- }
- // Construct expected URL based on server config *at time of verification*
+ if (!publicUrl) throw new Error('PUBLIC_URL environment variable is not configured on the server.');
expectedCallbackUrl = new URL('/api/digiid/callback', publicUrl).toString();
} catch (error) {
- // Handle errors during expected URL construction (e.g., invalid PUBLIC_URL)
console.error('Server configuration error constructing expected callback URL:', error);
session.status = 'failed';
session.error = 'Server configuration error preventing verification.';
- // Update store immediately on this specific failure
sessionStore.set(sessionId, session);
- // Respond 200 OK as per protocol, but status endpoint will show the config error
- return res.status(200).send();
+ // Respond 200 OK as per protocol
+ if (!res.headersSent) res.status(200).send();
+ return; // Explicitly return void
}
const verifyOptions = { expectedCallbackUrl, expectedNonce: session.nonce };
try {
console.log('Attempting to verify callback with:', { callbackData, verifyOptions });
- // verifyDigiIDCallback throws DigiIDError on failure
await verifyDigiIDCallback(callbackData, verifyOptions);
- // Success case
+ // Success case (no throw from verifyDigiIDCallback)
console.log(`Verification successful for session ${sessionId}, address: ${address}`);
session.status = 'success';
session.address = address; // Store the verified address
@@ -162,22 +157,22 @@ app.post('/api/digiid/callback', async (req: Request, res: Response) => {
// Failure case (verifyDigiIDCallback threw an error)
console.warn(`Verification failed for session ${sessionId}:`, error);
session.status = 'failed';
- if (error instanceof DigiIDError) {
- session.error = error.message; // Use message from DigiIDError
- } else if (error instanceof Error) {
- session.error = `Unexpected verification error: ${error.message}`;
+ if (error instanceof Error) {
+ session.error = error.message;
} else {
session.error = 'An unknown verification error occurred.';
}
// Optionally cleanup nonce map on failure too, depending on policy
// nonceToSessionMap.delete(session.nonce);
} finally {
- // Update store with final status (success/failed) and respond 200 OK
+ // Update store and respond 200 OK in all cases (after try/catch)
sessionStore.set(sessionId, session);
console.log(`Final session state for ${sessionId}:`, session);
- // Wallet expects 200 OK regardless of internal success/fail.
- // Client uses /status endpoint to get the actual result.
- res.status(200).send();
+ // Ensure response is sent if not already done (e.g. in case of unexpected error before finally)
+ if (!res.headersSent) {
+ res.status(200).send();
+ }
+ // No explicit return needed here as it's the end of the function
}
});
@@ -186,10 +181,9 @@ app.get('/api/digiid/status/:sessionId', (req: Request, res: Response) => {
const { sessionId } = req.params;
const session = sessionStore.get(sessionId);
if (!session) {
- // Session ID is unknown or expired (and cleaned up)
- return res.status(404).json({ status: 'not_found' });
+ res.status(404).json({ status: 'not_found' });
+ return; // Keep explicit return
}
- // Return only relevant fields to client
const { status, address, error } = session;
res.json({ status, address, error });
});
diff --git a/tsconfig.json b/tsconfig.json
index c3d78aa..15bc493 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -16,9 +16,10 @@
"paths": {
"~/*": ["./src/*"]
},
- "outDir": "dist",
- "noEmit": true
+ "outDir": "dist/server",
+ "rootDir": "src",
+ "noEmit": false
},
- "include": ["src", "vite.config.ts", ".eslintrc.cjs"],
- "exclude": ["node_modules", "dist"]
+ "include": ["src/server/**/*.ts", "src/shared/**/*.ts"],
+ "exclude": ["node_modules", "dist", "src/client"]
} \ No newline at end of file
diff --git a/vite.config.ts b/vite.config.ts
index 5e62e9a..69d80d1 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -6,9 +6,9 @@ export default defineConfig({
plugins: [react()],
server: {
proxy: {
- // Proxy /api requests to the backend server (running on port 3001 by default)
+ // Proxy /api requests to the backend server (running on port 3000 now)
'/api': {
- target: 'http://localhost:3001', // Default backend port, adjust if needed
+ target: 'http://localhost:3000', // Adjust to match backend PORT from .env
changeOrigin: true, // Recommended for virtual hosted sites
secure: false, // Don't verify SSL certs if backend uses self-signed cert in dev
},