require('dotenv').config(); const postgres = require('postgres'); const express = require('express'); const bodyParser = require('body-parser'); const app = express(); const Hashids = require('hashids'); const expressws = require('express-ws'); const { generateKey } = require('crypto'); const { promisify } = require('util'); const multer = require('multer'); const storage = multer.diskStorage({ destination: 'uploads/', filename: async (req, file, cb) => { const key = (await promisify(generateKey)('hmac', {length: 64})).export().toString('hex'); cb(null, key); } }); const EventEmitter = require('events'); const images = new EventEmitter(); const jsonParser = bodyParser.json(); const upload = multer({ storage: storage, fileFilter: (req, file, cb) => { if (!imageFormats.includes(file.mimetype)) return cb(null, false); cb(null, true); }}); const imageFormats = ['image/gif', 'image/jpeg', 'image/png', 'image/svg+xml', 'image/tiff', 'image/webp', 'image/bmp', 'image/avif', 'image/vnd.microsoft.icon']; const idLength = 6; const hashids = new Hashids('chartmaker', idLength); const port = process.env.PORT || 5252; const sql = postgres(`postgres://${process.env.DB_USER || 'chartmaker'}:${process.env.DB_PASS}@${process.env.DB_HOST || 'localhost'}:${process.env.DB_PORT || '5432'}/${process.env.DB_NAME || 'chartmaker'}`); expressws(app); app.use('/img', express.static('uploads')); app.use((req, res, next) => { res = res.setHeader('Access-Control-Allow-Origin', '*'); next(); }); app.post('/create', jsonParser, async (req, res) => { const [r] = await sql`SELECT id FROM charts ORDER BY id DESC LIMIT 1`; let newid = 1; if (r) newid = r.id + 1; console.log(req.body); let body = req.body || {}; await sql` INSERT INTO charts VALUES (${newid}, ${body.name || ''}, ${body.left || ''}, ${body.right || ''}, ${body.top || ''}, ${body.bot || ''}) `; res.status(200).send(hashids.encode(newid)); }); app.post('/add', upload.single('icon'), async (req, res) => { if (!req.body) return res.sendStatus(400); if (!req.file) return res.status(400).send('Send a file!'); if (!req.body.id) return res.status(400).send('Supply a chart ID!'); if (!req.body.name) return res.status(400).send('Supply a name!'); if (!req.body.x || !req.body.y) return res.status(400).send('Supply a position!'); const id = hashids.decode(req.body.id); if (!id[0]) return res.status(400).send('Invalid chart ID!'); let [exists] = await sql` SELECT id FROM charts WHERE id = ${id[0]} `; if (!exists) return res.status(400).send('Chart doesn\'t exist!'); await sql` INSERT INTO images VALUES (${id[0]}, ${req.file.filename}, ${req.body.name}, ${req.body.x}, ${req.body.y}) `; images.emit('imageAdd', id[0], {name: req.body.name, x: req.body.x, y: req.body.y, file: req.file.filename}) res.sendStatus(200); }); app.ws('/connect', async (ws, req) => { if (!req.query.id) return ws.close(1008, 'Supply a chart ID in the query!'); const id = hashids.decode(req.query.id)[0]; if (!id) return ws.close(1008, 'Invalid chart ID!'); let [chart] = await sql` SELECT * FROM charts WHERE id = ${id} `; if (!chart) return ws.close(1008, 'Chart doesn\'t exist!'); console.log(chart); await ws.send(JSON.stringify({name: chart.name, leftText: chart.lefttext, rightText: chart.righttext, topText: chart.toptext, bottomText: chart.bottomtext})); await ws.send(JSON.stringify((await sql`SELECT * FROM images WHERE id = ${id}`).map(i => {return {name: i.name, x: i.x, y: i.y, file: i.filepath}}))); const callback = (scrunklyId, scrunkly) => { console.log(scrunklyId, scrunkly); if (scrunklyId === id) { ws.send(JSON.stringify([scrunkly])); } }; images.on('imageAdd', callback); ws.on('close', () => {images.off('imageAdd', callback)}); }); (async () => { await sql` CREATE TABLE IF NOT EXISTS charts ( id int PRIMARY KEY, name text, leftText text, rightText text, topText text, bottomText text ) `; await sql` CREATE TABLE IF NOT EXISTS images ( id int, filepath text, name text, x real, y real ) `; app.listen(port, () => { console.log(`listening on ${port}`); }); })();