Files
tlsn-plugin-demo/server/index.tsx
2025-07-07 10:39:28 -07:00

159 lines
4.1 KiB
TypeScript

import express from 'express';
import React from 'react';
import { renderToString } from 'react-dom/server';
import { StaticRouter } from 'react-router-dom/server';
import App from '../web/pages/App';
import configureAppStore, { AppRootState } from '../web/store';
import { Provider } from 'react-redux';
import { Mutex } from 'async-mutex';
//@ts-ignore
import { verify } from '../rs/0.1.0-alpha.12/index.node';
import { convertNotaryWsToHttp, fetchPublicKeyFromNotary } from './util/index';
import { assignPoapToUser } from './util/index';
const app = express();
const port = 3031;
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader(
'Access-Control-Allow-Headers',
'Origin, X-Requested-With, Content, Accept, Content-Type, Authorization',
);
res.setHeader(
'Access-Control-Allow-Methods',
'GET, POST, PUT, DELETE, PATCH, OPTIONS',
);
res.setHeader('Cross-origin-Embedder-Policy', 'require-corp');
res.setHeader('Cross-origin-Opener-Policy', 'same-origin');
if (req.method === 'OPTIONS') {
res.sendStatus(200);
} else {
next();
}
});
app.use(express.static('build/ui'));
app.use(express.json());
const mutex = new Mutex();
app.get('*', (req, res) => {
try {
const storeConfig: AppRootState = {
attestation: {
raw: {
version: '0.1.0-alpha.12',
data: '',
meta: {
notaryUrl: '',
websocketProxyUrl: '',
pluginUrl: '',
},
},
},
};
const store = configureAppStore(storeConfig);
const html = renderToString(
<Provider store={store}>
<StaticRouter location={req.url}>
<App />
</StaticRouter>
</Provider>,
);
const preloadedState = store.getState();
res.send(`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/png" href="/favicon.png" />
<title>TLSN Plugin Demo</title>
<script>
window.__PRELOADED_STATE__ = ${JSON.stringify(preloadedState)};
</script>
<script defer src="/index.bundle.js"></script>
</head>
<body>
<div id="root">${html}</div>
<div id="modal-root"></div>
</body>
</html>
`);
} catch (e) {
res.send(`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/png" href="/favicon.png" />
<title>TLSN Plugin Demo</title>
<script>
window.__PRELOADED_STATE__ = {};
</script>
<script defer src="/index.bundle.js"></script>
</head>
<body>
<div id="root"></div>
<div id="modal-root"></div>
</body>
</html>
`);
}
});
app.post('/poap-claim', async (req, res) => {
const { screenName } = req.body;
if (!screenName) {
return res.status(400).json({ error: 'Missing screen_name' });
}
try {
await mutex.runExclusive(async () => {
const poapLink = await assignPoapToUser(screenName);
if (!poapLink) {
return res.status(404).json({ error: 'No POAPs available' });
}
return res.json({ poapLink });
});
} catch (error) {
console.error('Error claiming POAP:', error);
return res.status(500).json({ error: 'Internal server error' });
}
});
app.post('/verify-attestation', async (req, res) => {
const { attestation } = req.body;
if (!attestation) {
return res.status(400).send('Missing attestation');
}
try {
const notaryUrl = attestation.meta.notaryUrl;
const notaryPem = await fetchPublicKeyFromNotary(notaryUrl);
const presentation = await verify(attestation.data, notaryPem);
const presentationObj = {
sent: presentation.sent,
recv: presentation.recv,
};
res.json({ presentationObj });
} catch (e) {
console.error(e);
res.status(500).send('Error verifying attestation');
}
});
app.listen(port, () => {
console.log(`Plugin demo listening on port ${port}`);
});