diff options
author | Pawel Zelawski <pawel.zelawski@outlook.com> | 2025-04-10 14:35:56 +0200 |
---|---|---|
committer | Pawel Zelawski <pawel.zelawski@outlook.com> | 2025-04-10 14:35:56 +0200 |
commit | d858bd4e0fc7aaba15509a17b35138e843667bc1 (patch) | |
tree | b8251d1579bebe7128efe5b93ed734f9a75b234b | |
parent | e29a1b9a652fe254d13afef7f5c7a3d7a64b68cc (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.html | 12 | ||||
-rw-r--r-- | package.json | 6 | ||||
-rw-r--r-- | src/client/App.tsx | 101 | ||||
-rw-r--r-- | src/client/index.css | 301 | ||||
-rw-r--r-- | src/client/utils.ts | 33 | ||||
-rw-r--r-- | src/server/main.ts | 62 | ||||
-rw-r--r-- | tsconfig.json | 9 | ||||
-rw-r--r-- | vite.config.ts | 4 |
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 }, |