import 'dotenv/config';
import express from 'express';
import fileUpload from 'express-fileupload';
import stream from 'stream';
import { addBytes, getCID } from './services/ipfs';
import App from '../web/pages/App';
import { Provider } from 'react-redux';
import React from 'react';
import { renderToString } from 'react-dom/server';
import { StaticRouter } from 'react-router-dom/server';
import configureAppStore, { AppRootState } from '../web/store';
// @ts-ignore
import { verify } from '../rs/verifier/index.node';
// @ts-ignore
import { verify as verifyV7 } from '../rs/0.1.0-alpha.7/index.node';
import { Attestation } from '../web/utils/types/types';
import { IncomingMessage } from 'node:http';
import { createServer } from 'http';
import { WebSocketServer, type RawData, type WebSocket } from 'ws';
import crypto from 'crypto';
import qs from 'qs';
import { convertNotaryWsToHttp } from '../utils/url';
const app = express();
const port = process.env.PORT || 3000;
const server = createServer(app);
const wss = new WebSocketServer({ server });
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(
fileUpload({
limits: { fileSize: 1024 * 1024 }, // 1mb file limit
}),
);
app.post('/api/upload', async (req, res) => {
for (const file of Object.values(req.files!)) {
// @ts-ignore
const data = file.data;
const cid = await addBytes(data);
res.json(cid);
return;
}
res.status(400).send({ error: true, message: 'request is missing file' });
});
app.get('/gateway/ipfs/:cid', async (req, res) => {
const cid = req.params.cid;
const file = await getCID(req.params.cid);
const readStream = new stream.PassThrough();
readStream.end(Buffer.from(file));
res.set('Content-Type', 'application/octet-stream');
res.set('Content-Disposition', `attachment; filename=${cid}.json`);
readStream.pipe(res);
});
app.get('/ipfs/:cid', async (req, res) => {
// If there is no file from CID or JSON cannot be parsed, redirect to root
try {
const { cid } = req.params;
const [, isWasm] = cid.split('.');
if (isWasm) {
return res.redirect(`/${cid}`);
}
const storeConfig: AppRootState = {
notaryKey: { key: '' },
proofUpload: {
proofs: [],
selectedProof: null,
},
proofs: { ipfs: {} },
};
const file = await getCID(req.params.cid);
const jsonProof: Attestation = JSON.parse(file);
storeConfig.proofs.ipfs[req.params.cid] = {
raw: jsonProof,
};
/**
* Verify the proof if notary url exist
* redirect to root if verification fails
*/
if (!jsonProof.version && jsonProof.notaryUrl) {
const notaryPem = await fetchPublicKeyFromNotary(jsonProof.notaryUrl);
const proof = await verify(file, notaryPem);
proof.notaryUrl = jsonProof.notaryUrl;
storeConfig.proofs.ipfs[req.params.cid].proof = {
...proof,
version: '0.1.0-alpha.5',
notaryUrl: jsonProof.notaryUrl,
notaryKey: notaryPem,
};
} else if (jsonProof.version) {
const notaryUrl = convertNotaryWsToHttp(jsonProof.meta.notaryUrl);
const notaryPem = await fetchPublicKeyFromNotary(notaryUrl).catch(
() => '',
);
const proof = await verifyV7(jsonProof.data, notaryPem);
proof.notaryUrl = jsonProof.meta.notaryUrl;
storeConfig.proofs.ipfs[req.params.cid].proof = {
version: jsonProof.version,
time: proof.time,
sent: proof.sent,
recv: proof.recv,
notaryUrl: notaryUrl,
websocketProxyUrl: jsonProof.meta.websocketProxyUrl,
notaryKey: Buffer.from(
notaryPem
.replace('-----BEGIN PUBLIC KEY-----', '')
.replace('-----END PUBLIC KEY-----', '')
.replace(/\n/g, ''),
'base64',
)
.slice(23)
.toString('hex'),
};
}
const store = configureAppStore(storeConfig);
const html = renderToString(