import {PutObjectCommand, S3Client} from "@aws-sdk/client-s3"; import cors from 'cors' import crypto from "crypto"; import {sql} from "./db.js"; import {decodeBase62} from "../web/src/common.js"; const APP_URL = process.env.DEXORDER_APP_URL; const SNAPSHOT_URL = process.env.DEXORDER_SNAPSHOT_URL; const S3_BUCKET = process.env.DEXORDER_SNAPSHOT_S3_BUCKET_NAME; const S3_ACCESS_KEY_ID = process.env.DEXORDER_SNAPSHOT_S3_ACCESS_KEY_ID; const S3_SECRET_ACCESS_KEY = process.env.DEXORDER_SNAPSHOT_S3_SECRET_ACCESS_KEY; const S3_ENDPOINT = process.env.DEXORDER_SNAPSHOT_S3_ENDPOINT; // e.g., 'https://.r2.cloudflarestorage.com/' const s3 = new S3Client({ region: "auto", endpoint: S3_ENDPOINT, credentials: { accessKeyId: S3_ACCESS_KEY_ID, secretAccessKey: S3_SECRET_ACCESS_KEY, } }); function imageFilename(code) { return 'share_' + code + '.png'; } async function saveSnapshot(code, snapshot) { await s3.send(new PutObjectCommand({ Bucket: S3_BUCKET, Key: imageFilename(code), Body: snapshot, ContentType: 'image/png', ACL: 'public-read', // or private, depending on your needs })) } export async function share(socket, data, snapshot, respond) { try { const result = await sql('insert into sharedata (data) values ($1) returning id', data) if (result.rowCount !== 1) { console.error('insertion of sharedata failed', result) respond(null) } const code = encodeURIComponent(result.rows[0].id) respond(code); saveSnapshot(code, snapshot).catch(e => console.error('save snapshot error', e)) } catch (e) { console.error('share error', e) respond(null) } } export async function shared(socket, code, respond) { try { const id = decodeBase62(code) const result = await sql('select data from sharedata where id = $1', id) if (result.rowCount !== 1) { console.error('could not find share data', code) respond(null) } const data = result.rows[0].data console.log('shared data', data) respond(data) } catch (e) { console.error('shared error', e) respond(null) } } export function initSnapShare(app) { // this URL is called by the frontend to upload order data and a snapshot image for use on a share page app.post('/sharecode', cors({origin: process.env.DEXORDER_APP_URL}), (req, res) => { const chunks = []; req.on('data', chunk => chunks.push(chunk)) req.on('error', (err) => res.status(500).send('Error reading body')) req.on('end', () => { const filename = crypto.randomUUID() + '.png'; const body = Buffer.concat(chunks); s3.send(new PutObjectCommand({ Bucket: S3_BUCKET, Key: filename, Body: body, ContentType: 'image/png', ACL: 'public-read', // or private, depending on your needs })).then(sent => { res.send(filename) }).catch(err => { console.log('upload error', err) res.status(500).send('error') }); }); }); // this link returns a "share page" that shows the snapshot of the trade setup then redirects // to the order page with the trade data loaded from the URL app.get('/share/:code', (req, res) => { const code = req.params.code; const data = { imageUrl: SNAPSHOT_URL + '/' + imageFilename(code), redirectUrl: APP_URL + '/shared/' + code }; res.render('share', data); }); }