initial version
						commit
						ae5b63f1ea
					
				|  | @ -0,0 +1,2 @@ | ||||||
|  | node_modules | ||||||
|  | vybe.db | ||||||
|  | @ -0,0 +1,68 @@ | ||||||
|  | # vybe websocket protocol - sent by client | ||||||
|  | 
 | ||||||
|  | socket.io actions + expected msg format and other info | ||||||
|  | 
 | ||||||
|  | all responses have same type as original message | ||||||
|  | 
 | ||||||
|  | if a message fails, you get a response with the following format: | ||||||
|  | 
 | ||||||
|  | ```json | ||||||
|  | { | ||||||
|  |   "success": false, | ||||||
|  |   "message": "Something broke" | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ## create_user | ||||||
|  | 
 | ||||||
|  | Message format: | ||||||
|  | 
 | ||||||
|  | ```json | ||||||
|  | { | ||||||
|  |   "name": "unique_username", | ||||||
|  |   "pubkey": "your PGP public key" | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | Response format: | ||||||
|  | 
 | ||||||
|  | ```json | ||||||
|  | { | ||||||
|  |   "success": true, | ||||||
|  |   "id": 123 | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ## get_history | ||||||
|  | 
 | ||||||
|  | todo | ||||||
|  | 
 | ||||||
|  | ## send_message | ||||||
|  | 
 | ||||||
|  | Message format: | ||||||
|  | 
 | ||||||
|  | ```json | ||||||
|  | { | ||||||
|  |   "name": "unique_username", | ||||||
|  |   "message": "signed message" | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | Response format: | ||||||
|  | 
 | ||||||
|  | ```json | ||||||
|  | { | ||||||
|  |   "success": true | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | # sent by server | ||||||
|  | 
 | ||||||
|  | ## new_message | ||||||
|  | 
 | ||||||
|  | ```json | ||||||
|  | { | ||||||
|  |   "name": "unique_username", | ||||||
|  |   "message": "msg text" | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  | @ -0,0 +1,13 @@ | ||||||
|  | # vybe server | ||||||
|  | 
 | ||||||
|  | very very unfinished !! right now it can do a chatroom | ||||||
|  | 
 | ||||||
|  | ## setup | ||||||
|  | 
 | ||||||
|  | currently uses sqlite! install sqlite for your machine then do the following | ||||||
|  | 
 | ||||||
|  | ```bash | ||||||
|  | sqlite3 vybe.db < db/1-init.sql | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | then you can `yarn install` and `node index.js` | ||||||
|  | @ -0,0 +1,14 @@ | ||||||
|  | const sqlite3 = require("sqlite3"); | ||||||
|  | const db = new sqlite3.Database("./vybe.db"); | ||||||
|  | 
 | ||||||
|  | db.query = function (sql, params) { | ||||||
|  |   var that = this; | ||||||
|  |   return new Promise(function (resolve, reject) { | ||||||
|  |     that.all(sql, params, function (error, rows) { | ||||||
|  |       if (error) reject(error); | ||||||
|  |       else resolve({ rows: rows }); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | module.exports = db; | ||||||
|  | @ -0,0 +1,24 @@ | ||||||
|  | CREATE TABLE users ( | ||||||
|  |   id integer primary key asc, | ||||||
|  |   name text, | ||||||
|  |   pubkey text, | ||||||
|  |   created timestamp default current_timestamp | ||||||
|  | ); | ||||||
|  | 
 | ||||||
|  | CREATE TABLE threads ( | ||||||
|  |   id integer primary key asc, | ||||||
|  |   created timestamp default current_timestamp | ||||||
|  | ); | ||||||
|  | 
 | ||||||
|  | CREATE TABLE posts ( | ||||||
|  |   id integer primary key asc, | ||||||
|  |   user integer, | ||||||
|  |   thread integer, | ||||||
|  |   content text, | ||||||
|  |   sig text, | ||||||
|  |   created timestamp default current_timestamp, | ||||||
|  |   foreign key(user) references users(id), | ||||||
|  |   foreign key(thread) references threads(id) | ||||||
|  | ); | ||||||
|  | 
 | ||||||
|  | INSERT INTO threads default values; | ||||||
|  | @ -0,0 +1,26 @@ | ||||||
|  | const express = require("express"); | ||||||
|  | const app = express(); | ||||||
|  | const http = require("http"); | ||||||
|  | const server = http.createServer(app); | ||||||
|  | const { Server } = require("socket.io"); | ||||||
|  | const io = new Server(server, { | ||||||
|  |   cors: { | ||||||
|  |     origin: ["http://localhost:8000", "http://192.168.1.199:8000"], | ||||||
|  |   }, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const PORT = process.env.PORT || 3435; | ||||||
|  | 
 | ||||||
|  | const actions = require("./src/actions"); | ||||||
|  | 
 | ||||||
|  | io.on("connection", (socket) => { | ||||||
|  |   for (let action in actions) { | ||||||
|  |     socket.on(action, (msg) => | ||||||
|  |       actions[action](msg, (response) => socket.emit(action, response), socket) | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | server.listen(PORT, () => { | ||||||
|  |   console.log("server running on port " + PORT); | ||||||
|  | }); | ||||||
|  | @ -0,0 +1,18 @@ | ||||||
|  | { | ||||||
|  |   "name": "server", | ||||||
|  |   "version": "1.0.0", | ||||||
|  |   "description": "", | ||||||
|  |   "main": "index.js", | ||||||
|  |   "scripts": { | ||||||
|  |     "test": "echo \"Error: no test specified\" && exit 1" | ||||||
|  |   }, | ||||||
|  |   "author": "", | ||||||
|  |   "license": "ISC", | ||||||
|  |   "dependencies": { | ||||||
|  |     "cors": "^2.8.5", | ||||||
|  |     "express": "^4.18.2", | ||||||
|  |     "openpgp": "^5.8.0", | ||||||
|  |     "socket.io": "^4.6.1", | ||||||
|  |     "sqlite3": "^5.1.6" | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,9 @@ | ||||||
|  | const create_user = require("./create_user"); | ||||||
|  | const get_history = require("./get_history"); | ||||||
|  | const send_message = require("./send_message"); | ||||||
|  | 
 | ||||||
|  | module.exports = { | ||||||
|  |   create_user, | ||||||
|  |   get_history, | ||||||
|  |   send_message, | ||||||
|  | }; | ||||||
|  | @ -0,0 +1,49 @@ | ||||||
|  | const db = require("../db"); | ||||||
|  | const openpgp = require("openpgp"); | ||||||
|  | 
 | ||||||
|  | const create_user = async (msg, respond) => { | ||||||
|  |   // validate inputs
 | ||||||
|  |   if (!msg.name) { | ||||||
|  |     return respond({ | ||||||
|  |       success: false, | ||||||
|  |       message: "Username required", | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  |   // ensure username is not taken
 | ||||||
|  |   const result = await db.query("select * from users where name = ?", [ | ||||||
|  |     msg.name, | ||||||
|  |   ]); | ||||||
|  |   if (result.rows.length > 0) { | ||||||
|  |     return respond({ | ||||||
|  |       success: false, | ||||||
|  |       message: "A user with this name already exists on this server", | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  |   // validate public key
 | ||||||
|  |   if (!msg.pubkey) { | ||||||
|  |     return respond({ | ||||||
|  |       success: false, | ||||||
|  |       message: "Public key required", | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  |   try { | ||||||
|  |     await openpgp.readKey({ armoredKey: msg.pubkey }); | ||||||
|  |   } catch (err) { | ||||||
|  |     return respond({ | ||||||
|  |       success: false, | ||||||
|  |       message: "Public key invalid", | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  |   // add to db
 | ||||||
|  |   const insert = await db.query( | ||||||
|  |     "insert into users (name, pubkey) values (?, ?) returning id", | ||||||
|  |     [msg.name, msg.pubkey] | ||||||
|  |   ); | ||||||
|  |   // respond
 | ||||||
|  |   return respond({ | ||||||
|  |     success: true, | ||||||
|  |     id: insert.rows[0].id, | ||||||
|  |   }); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | module.exports = create_user; | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | const get_history = async (msg) => {}; | ||||||
|  | 
 | ||||||
|  | module.exports = get_history; | ||||||
|  | @ -0,0 +1,42 @@ | ||||||
|  | const db = require("../db"); | ||||||
|  | const openpgp = require("openpgp"); | ||||||
|  | 
 | ||||||
|  | const send_message = async (msg, respond, socket) => { | ||||||
|  |   // find user
 | ||||||
|  |   const result = await db.query("select * from users where name = ?", [ | ||||||
|  |     msg.name, | ||||||
|  |   ]); | ||||||
|  |   if (result.rows.length === 0) { | ||||||
|  |     return respond({ | ||||||
|  |       success: false, | ||||||
|  |       message: "User not found", | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  |   // validate signature
 | ||||||
|  |   try { | ||||||
|  |     const key = await openpgp.readKey({ armoredKey: result.rows[0].pubkey }); | ||||||
|  |     const verification = await openpgp.verify({ | ||||||
|  |       message: await openpgp.readCleartextMessage({ | ||||||
|  |         cleartextMessage: msg.message, | ||||||
|  |       }), | ||||||
|  |       verificationKeys: key, | ||||||
|  |       expectSigned: true, | ||||||
|  |     }); | ||||||
|  |     // add message and send it to everyone
 | ||||||
|  |     await db.query( | ||||||
|  |       "insert into posts (user, thread, content, sig) values (?, 1, ?, ?)", | ||||||
|  |       [result.rows[0].id, verification.data, msg.message] | ||||||
|  |     ); | ||||||
|  |     socket.broadcast.emit("new_message", { | ||||||
|  |       name: msg.name, | ||||||
|  |       message: verification.data, | ||||||
|  |     }); | ||||||
|  |   } catch (err) { | ||||||
|  |     return respond({ | ||||||
|  |       success: false, | ||||||
|  |       message: "Message signature verification failed", | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | module.exports = send_message; | ||||||
		Loading…
	
		Reference in New Issue