diff --git a/.env.example b/.env.example index b2f91e96f0ce4588aa9d68673a568d750b88c121..fee4edc9b50cdd0e52b40847367f90dd6cb2a5f2 100644 --- a/.env.example +++ b/.env.example @@ -12,3 +12,6 @@ DB_PASSWORD=my_db_password # === HASURA === HASURA_GRAPHQL_ADMIN_SECRET=my_hasura_password HASURA_LISTEN_PORT=8765 + +# === GATEWAY +GATEWAY_LISTEN_PORT=3000 \ No newline at end of file diff --git a/src/gateway/README.md b/src/gateway/README.md new file mode 100644 index 0000000000000000000000000000000000000000..2af5a1c0048e766302abd256062407f9c334db82 --- /dev/null +++ b/src/gateway/README.md @@ -0,0 +1,7 @@ +# Gateway + +This gateway is a way for non-p2p clients to submit data to a http endpoint that will then be forwarded to the p2p network. + +It is a stupid gateway that puts all content to local kubo node (but does not pin) and forwards the cid of the first block on pubsub topic. + +Since it does not include any protection mechanism because it relies on datapod network security, it is recommanded to limit the incoming network traffic and apply IP-based protections, preventing an attacker to trigger IPFS garbage collection too easily. Or to limit connection to trusted users. \ No newline at end of file diff --git a/src/gateway/start.ts b/src/gateway/start.ts new file mode 100644 index 0000000000000000000000000000000000000000..0203a4a94ed3685d03d5686f89133799a5a37a95 --- /dev/null +++ b/src/gateway/start.ts @@ -0,0 +1,71 @@ +import { TOPIC } from '../consts' +import { kubo } from '../kubo' +import * as http from 'http' + +// we could use this hack to allow submitting json data with html form +// https://systemoverlord.com/2016/08/24/posting-json-with-an-html-form.html +const GATEWAY_LANDING_PAGE = `<!DOCTYPE html> +<html> + <head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"/> + <title>Datapod Gateway</title> + </head> + <body> + <h1>Datapod Gateway</h1> + <p>Use POST requests to submit data programatically. Alternatively, you can submit data manually in the form below for testing purpose.</p> + <form method="post" action="http://localhost:3000" enctype='text/plain'> + Blocks as base64 json list: + <input type="text" name="blocks" size=100 placeholder="['index request as base64', 'data as base 64', 'optional aditionnal data as base64'...]"/> + <input type="submit" value="Submit" /> + </form> + </body> +</html> +` + +function handleRequest(request: http.IncomingMessage, response: http.ServerResponse<http.IncomingMessage>) { + if (request.method == 'POST') { + let body = '' + request.on('data', function (data) { + body += data + }) + request.on('end', function () { + // hack to allow submitting data with form + if (body.startsWith('blocks=')) { + body = body.slice(7) + } + console.log('Body: ' + body) + try { + const blocks = JSON.parse(body) + let first = true + for (const b of blocks) { + // first item is the index request CID + if (first) { + kubo.pubsub.publish(TOPIC, new TextEncoder().encode(b + '\n')) + first = false + continue + } + // other items are blocks + const buffer = Buffer.from(b, 'base64') + const bytes = new Uint8Array(buffer) + kubo.block.put(bytes).then((cid) => {}) + } + response.writeHead(200, { 'Content-Type': 'text/html' }) + response.end('data received and forwarded<br/>' + JSON.stringify(blocks, null, 4)) + } catch (e: any) { + response.writeHead(400, { 'Content-Type': 'text/html' }) + response.end('invalid request\n' + e.toString()) + } + }) + } else { + var html = GATEWAY_LANDING_PAGE + response.writeHead(200, { 'Content-Type': 'text/html' }) + response.end(html) + } +} + +const server = http.createServer(handleRequest) +const port = process.env.GATEWAY_LISTEN_PORT || 3000 +const host = '127.0.0.1' +server.listen(port) +console.log(`Listening on http://${host}:${port}`)