diff --git a/projects/TokenizeRWATemplate-contracts/NFT_mint_server/app.js b/projects/TokenizeRWATemplate-contracts/NFT_mint_server/app.js index b2f159d..a94e7d8 100644 --- a/projects/TokenizeRWATemplate-contracts/NFT_mint_server/app.js +++ b/projects/TokenizeRWATemplate-contracts/NFT_mint_server/app.js @@ -1,3 +1,4 @@ +// Shared Express app (no .listen here) import pinataSDK from '@pinata/sdk' import cors from 'cors' import dotenv from 'dotenv' @@ -13,7 +14,7 @@ const app = express() /** * CORS * - Allows localhost dev - * - Allows your main frontend + * - Allows your main frontend(s) via ALLOWED_ORIGINS * - Allows ANY *.vercel.app (so forks work for non-technical founders) * * Optional: set ALLOWED_ORIGINS in Vercel env as comma-separated list @@ -25,8 +26,9 @@ const explicitAllowed = (process.env.ALLOWED_ORIGINS || '') .map((s) => s.trim()) .filter(Boolean) -const isAllowedOrigin = (origin: string) => { +function isAllowedOrigin(origin: string) { // explicitly allowed + if (explicitAllowed.includes('*')) return true if (explicitAllowed.includes(origin)) return true // local dev @@ -37,27 +39,33 @@ const isAllowedOrigin = (origin: string) => { const host = new URL(origin).hostname if (host.endsWith('.vercel.app')) return true } catch { - // ignore + // ignore bad origins } return false } -app.use( - cors({ - origin: (origin, cb) => { - // allow server-to-server / curl / same-origin (no Origin header) - if (!origin) return cb(null, true) - if (isAllowedOrigin(origin)) return cb(null, true) - return cb(null, false) - }, - methods: ['GET', 'POST', 'OPTIONS'], - allowedHeaders: ['Content-Type', 'Authorization'], - }), -) +const corsOptions: cors.CorsOptions = { + origin: (origin, cb) => { + // allow server-to-server / curl / same-origin (no Origin header) + if (!origin) return cb(null, true) -// Handle preflight requests for ALL routes -app.options('*', cors()) + if (isAllowedOrigin(origin)) return cb(null, true) + + // IMPORTANT: return an error (not "false") so it's obvious in logs/debugging + return cb(new Error(`CORS blocked for origin: ${origin}`)) + }, + methods: ['GET', 'POST', 'OPTIONS'], + allowedHeaders: ['Content-Type', 'Authorization'], + credentials: false, + optionsSuccessStatus: 204, +} + +// Apply CORS to all routes +app.use(cors(corsOptions)) + +// Handle preflight requests for ALL routes (with the SAME options) +app.options('*', cors(corsOptions)) app.use(express.json()) @@ -67,6 +75,16 @@ const pinata = ? new pinataSDK({ pinataJWTKey: process.env.PINATA_JWT }) : new pinataSDK(process.env.PINATA_API_KEY || '', process.env.PINATA_API_SECRET || '') +// Optional: test credentials at cold start (helps a LOT on Vercel) +;(async () => { + try { + const auth = await (pinata as any).testAuthentication?.() + console.log('Pinata auth OK:', auth || 'ok') + } catch (e) { + console.error('Pinata authentication FAILED. Check env vars.', e) + } +})() + // Uploads (multipart/form-data) const upload = multer({ storage: multer.memoryStorage(), diff --git a/projects/TokenizeRWATemplate-frontend/package-lock.json b/projects/TokenizeRWATemplate-frontend/package-lock.json index 2b56109..2b13a7f 100644 --- a/projects/TokenizeRWATemplate-frontend/package-lock.json +++ b/projects/TokenizeRWATemplate-frontend/package-lock.json @@ -14,6 +14,7 @@ "@perawallet/connect": "^1.4.1", "@txnlab/use-wallet": "^4.4.0", "@txnlab/use-wallet-react": "^4.4.0", + "@vercel/analytics": "^1.6.1", "@web3auth/base": "^9.7.0", "@web3auth/base-provider": "^9.7.0", "@web3auth/modal": "^9.7.0", @@ -4210,6 +4211,44 @@ ], "peer": true }, + "node_modules/@vercel/analytics": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@vercel/analytics/-/analytics-1.6.1.tgz", + "integrity": "sha512-oH9He/bEM+6oKlv3chWuOOcp8Y6fo6/PSro8hEkgCW3pu9/OiCXiUpRUogDh3Fs3LH2sosDrx8CxeOLBEE+afg==", + "license": "MPL-2.0", + "peerDependencies": { + "@remix-run/react": "^2", + "@sveltejs/kit": "^1 || ^2", + "next": ">= 13", + "react": "^18 || ^19 || ^19.0.0-rc", + "svelte": ">= 4", + "vue": "^3", + "vue-router": "^4" + }, + "peerDependenciesMeta": { + "@remix-run/react": { + "optional": true + }, + "@sveltejs/kit": { + "optional": true + }, + "next": { + "optional": true + }, + "react": { + "optional": true + }, + "svelte": { + "optional": true + }, + "vue": { + "optional": true + }, + "vue-router": { + "optional": true + } + } + }, "node_modules/@vitejs/plugin-react": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", diff --git a/projects/TokenizeRWATemplate-frontend/package.json b/projects/TokenizeRWATemplate-frontend/package.json index db91571..a08d7c9 100644 --- a/projects/TokenizeRWATemplate-frontend/package.json +++ b/projects/TokenizeRWATemplate-frontend/package.json @@ -43,6 +43,7 @@ "@perawallet/connect": "^1.4.1", "@txnlab/use-wallet": "^4.4.0", "@txnlab/use-wallet-react": "^4.4.0", + "@vercel/analytics": "^1.6.1", "@web3auth/base": "^9.7.0", "@web3auth/base-provider": "^9.7.0", "@web3auth/modal": "^9.7.0", diff --git a/projects/TokenizeRWATemplate-frontend/src/App.tsx b/projects/TokenizeRWATemplate-frontend/src/App.tsx index 6a7de60..983db2e 100644 --- a/projects/TokenizeRWATemplate-frontend/src/App.tsx +++ b/projects/TokenizeRWATemplate-frontend/src/App.tsx @@ -1,4 +1,5 @@ import { SupportedWallet, WalletId, WalletManager, WalletProvider } from '@txnlab/use-wallet-react' +import { Analytics } from '@vercel/analytics/next' import { SnackbarProvider } from 'notistack' import { useMemo } from 'react' import { BrowserRouter, Route, Routes } from 'react-router-dom' @@ -79,6 +80,7 @@ export default function App() { + }> } />