before
This commit is contained in:
parent
8fb7a3ae1e
commit
a78b796c55
21
client/.eslintrc.js
Executable file
21
client/.eslintrc.js
Executable file
@ -0,0 +1,21 @@
|
|||||||
|
module.exports = {
|
||||||
|
extends: 'airbnb-base',
|
||||||
|
rules: {
|
||||||
|
// prevents stupid complaints a la
|
||||||
|
// (req) {
|
||||||
|
// req.something = x;
|
||||||
|
// }
|
||||||
|
'no-param-reassign': [2, {
|
||||||
|
props: false,
|
||||||
|
}],
|
||||||
|
'no-multi-spaces': [0],
|
||||||
|
'max-len': ['error', 120],
|
||||||
|
'import/no-extraneous-dependencies': [0],
|
||||||
|
'prefer-arrow-callback': [0],
|
||||||
|
'arrow-body-style': [0],
|
||||||
|
'no-console': [0],
|
||||||
|
// i like loops
|
||||||
|
'no-plusplus': [0],
|
||||||
|
'no-await-in-loop': [0],
|
||||||
|
},
|
||||||
|
};
|
||||||
136
client/index.js
136
client/index.js
@ -1,63 +1,75 @@
|
|||||||
const cbor = require('borc');
|
const { toast } = require('bulma-toast');
|
||||||
const assert = require('assert');
|
|
||||||
// Create WebSocket connection.
|
const cbor = require('borc');
|
||||||
const ws = new WebSocket('ws://localhost:40000');
|
const assert = require('assert');
|
||||||
ws.binaryType = 'arraybuffer';
|
// Create WebSocket connection.
|
||||||
|
const ws = new WebSocket('ws://localhost:40000');
|
||||||
// handle user auth within the socket itself
|
ws.binaryType = 'arraybuffer';
|
||||||
// https://www.christian-schneider.net/CrossSiteWebSocketHijacking.html
|
|
||||||
let user = null;
|
// handle user auth within the socket itself
|
||||||
|
// https://www.christian-schneider.net/CrossSiteWebSocketHijacking.html
|
||||||
function user_login(res) {
|
let user = null;
|
||||||
[struct, created] = res;
|
|
||||||
|
function error_toast(err) {
|
||||||
user = created;
|
console.error(err);
|
||||||
|
return toast({
|
||||||
console.log(created);
|
message: err,
|
||||||
return send({ method: 'cryp_spawn', params: { level: 64 }});
|
type: "is-warning",
|
||||||
}
|
duration: 5000,
|
||||||
|
});
|
||||||
function new_cryp(cryp) {
|
}
|
||||||
console.log('got a new cryp');
|
|
||||||
}
|
function user_login(res) {
|
||||||
|
[struct, user] = res;
|
||||||
const handlers = {
|
|
||||||
'cryp_spawn': new_cryp,
|
user = user;
|
||||||
'user_login': user_login,
|
|
||||||
'user_create': user_login,
|
console.log(user);
|
||||||
};
|
return send({ method: 'cryp_spawn', params: { name: 'drake' }});
|
||||||
|
}
|
||||||
function receive(res) {
|
|
||||||
if (res.err) return console.error(res.err);
|
function new_cryp(cryp) {
|
||||||
const { method, params } = res;
|
console.log('got a new cryp');
|
||||||
return handlers[method](params);
|
}
|
||||||
}
|
|
||||||
|
const handlers = {
|
||||||
function send(msg) {
|
'cryp_spawn': new_cryp,
|
||||||
msg.token = user && user.token;
|
'user_login': user_login,
|
||||||
ws.send(cbor.encode(msg));
|
'user_create': user_login,
|
||||||
}
|
};
|
||||||
|
|
||||||
// Connection opened
|
function on_message(event) {
|
||||||
ws.addEventListener('open', function (event) {
|
// decode binary msg from server
|
||||||
send({ method: 'user_login', params: { name: 'somebodyelse', password: 'grepgrepgrep' }});
|
const blob = new Uint8Array(event.data);
|
||||||
});
|
const res = cbor.decode(blob);
|
||||||
|
console.log(res);
|
||||||
// Listen for messages
|
|
||||||
ws.addEventListener('message', function (event) {
|
// check for error and split into response type and data
|
||||||
console.log('Message from server ', event.data);
|
if (res.err) return error_toast(res.err);
|
||||||
const blob = new Uint8Array(event.data);
|
const { method, params } = res;
|
||||||
const decoded = cbor.decode(blob);
|
return handlers[method](params);
|
||||||
console.log(decoded);
|
}
|
||||||
return receive(decoded);
|
|
||||||
});
|
function send(msg) {
|
||||||
|
msg.token = user && user.token;
|
||||||
ws.addEventListener('error', function (event) {
|
ws.send(cbor.encode(msg));
|
||||||
console.error('WebSocket error', event);
|
}
|
||||||
user = null;
|
|
||||||
});
|
// Connection opened
|
||||||
|
ws.addEventListener('open', function (event) {
|
||||||
ws.addEventListener('close', function (event) {
|
// send({ method: 'user_create', params: { name: 'ntr', password: 'grepgrepgrep' }});
|
||||||
console.error('WebSocket closed', event);
|
send({ method: 'user_login', params: { name: 'ntr', password: 'grepgrepgrep' }});
|
||||||
user = null;
|
});
|
||||||
|
|
||||||
|
// Listen for messages
|
||||||
|
ws.addEventListener('message', on_message);
|
||||||
|
|
||||||
|
ws.addEventListener('error', function (event) {
|
||||||
|
console.error('WebSocket error', event);
|
||||||
|
user = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
ws.addEventListener('close', function (event) {
|
||||||
|
console.error('WebSocket closed', event);
|
||||||
|
user = null;
|
||||||
});
|
});
|
||||||
@ -11,6 +11,12 @@
|
|||||||
"license": "UNLICENSED",
|
"license": "UNLICENSED",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"borc": "^2.0.3",
|
"borc": "^2.0.3",
|
||||||
"parcel": "^1.9.7"
|
"bulma-toast": "^1.2.0",
|
||||||
|
"parcel": "^1.9.7",
|
||||||
|
"docco": "^0.7.0",
|
||||||
|
"eslint": "^3.18.0",
|
||||||
|
"eslint-config-airbnb-base": "^11.1.1",
|
||||||
|
"eslint-plugin-import": "^2.2.0",
|
||||||
|
"jest": "^18.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
exports.up = async knex => {
|
exports.up = async knex => {
|
||||||
return knex.schema.createTable('users', table => {
|
return knex.schema.createTable('accounts', table => {
|
||||||
table.uuid('id').primary();
|
table.uuid('id').primary();
|
||||||
table.string('name', 42).notNullable().unique();
|
table.string('name', 42).notNullable().unique();
|
||||||
table.string('password').notNullable();
|
table.string('password').notNullable();
|
||||||
14
ops/migrations/20180916221309_cryps_table.js
Executable file
14
ops/migrations/20180916221309_cryps_table.js
Executable file
@ -0,0 +1,14 @@
|
|||||||
|
exports.up = async knex => {
|
||||||
|
return knex.schema.createTable('cryps', table => {
|
||||||
|
table.uuid('id').primary();
|
||||||
|
table.uuid('account').notNullable()
|
||||||
|
table.foreign('account')
|
||||||
|
.references('id')
|
||||||
|
.inTable('accounts')
|
||||||
|
.onDelete('CASCADE');
|
||||||
|
table.binary('data').notNullable();
|
||||||
|
table.index('id');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.down = async () => {};
|
||||||
@ -1,5 +1,5 @@
|
|||||||
* Battling
|
* Battling
|
||||||
* Logins
|
* Logins ✔️
|
||||||
* Cryp Ownership
|
* Cryp Ownership
|
||||||
* Matchmaking
|
* Matchmaking
|
||||||
* Lobbies
|
* Lobbies
|
||||||
@ -19,5 +19,10 @@
|
|||||||
|
|
||||||
teams
|
teams
|
||||||
|
|
||||||
|
physical, magic, pure dmg?
|
||||||
|
elemental?
|
||||||
|
|
||||||
|
items give skills
|
||||||
|
|
||||||
gem td style attr combinations
|
gem td style attr combinations
|
||||||
stoney + spikey = jagged
|
stoney + spikey = jagged
|
||||||
@ -1,7 +1,13 @@
|
|||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use serde_cbor::*;
|
use serde_cbor::*;
|
||||||
|
use serde_cbor::{to_vec};
|
||||||
|
|
||||||
|
use failure::Error;
|
||||||
|
use failure::err_msg;
|
||||||
|
|
||||||
|
use net::Db;
|
||||||
|
use user::User;
|
||||||
use rpc::{CrypSpawnParams};
|
use rpc::{CrypSpawnParams};
|
||||||
use skill::{Skill};
|
use skill::{Skill};
|
||||||
|
|
||||||
@ -176,12 +182,32 @@ impl Cryp {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn spawn(params: CrypSpawnParams) -> Cryp {
|
pub fn spawn(params: CrypSpawnParams, db: Db, user: User) -> Result<Cryp, Error> {
|
||||||
Cryp::new()
|
let cryp = Cryp::new()
|
||||||
.named("hatchling".to_string())
|
.named(params.name)
|
||||||
.level(params.level)
|
.level(1)
|
||||||
.learn(Skill::Stoney)
|
.create();
|
||||||
.create()
|
|
||||||
|
let cryp_bytes = to_vec(&cryp)?;
|
||||||
|
|
||||||
|
let query = "
|
||||||
|
INSERT INTO cryps (id, user, data)
|
||||||
|
VALUES ($1, $2, $3)
|
||||||
|
RETURNING id, user;
|
||||||
|
";
|
||||||
|
|
||||||
|
let tx = db.transaction()?;
|
||||||
|
|
||||||
|
let result = tx
|
||||||
|
.query(query, &[&cryp.id, &user.id, &cryp_bytes])?;
|
||||||
|
|
||||||
|
let _returned = result.iter().next().expect("no row returned");
|
||||||
|
|
||||||
|
println!("{:?} spawned cryp {:}", user.id, cryp.id);
|
||||||
|
|
||||||
|
tx.commit()?;
|
||||||
|
|
||||||
|
return Ok(cryp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -11,7 +11,7 @@ static DB_POOL_SIZE: u32 = 20;
|
|||||||
|
|
||||||
pub type Db = PooledConnection<PostgresConnectionManager>;
|
pub type Db = PooledConnection<PostgresConnectionManager>;
|
||||||
|
|
||||||
use rpc::{Rpc, RpcResult};
|
use rpc::{Rpc};
|
||||||
|
|
||||||
struct Server {
|
struct Server {
|
||||||
out: Sender,
|
out: Sender,
|
||||||
@ -39,6 +39,7 @@ impl Handler for Server {
|
|||||||
self.out.send(response)
|
self.out.send(response)
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
println!("{:?}", e);
|
||||||
let response = to_vec(&RpcErrorResponse { err: e.to_string() })
|
let response = to_vec(&RpcErrorResponse { err: e.to_string() })
|
||||||
.expect("failed to serialize error response");
|
.expect("failed to serialize error response");
|
||||||
self.out.send(response)
|
self.out.send(response)
|
||||||
|
|||||||
@ -5,7 +5,7 @@ use failure::err_msg;
|
|||||||
|
|
||||||
use net::Db;
|
use net::Db;
|
||||||
use cryp::{Cryp, spawn};
|
use cryp::{Cryp, spawn};
|
||||||
use user::{User, create, login};
|
use user::{User, create, login, from_token};
|
||||||
|
|
||||||
pub struct Rpc;
|
pub struct Rpc;
|
||||||
|
|
||||||
@ -18,17 +18,27 @@ impl Rpc {
|
|||||||
match from_slice::<RpcMessage>(&data) {
|
match from_slice::<RpcMessage>(&data) {
|
||||||
Ok(v) => {
|
Ok(v) => {
|
||||||
|
|
||||||
println!("{:?}", v.token);
|
let user: Option<User> = match v.token {
|
||||||
|
Some(t) => Some(from_token(t, &db)?),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("{:?}", user);
|
||||||
|
|
||||||
// now we have the method name
|
// now we have the method name
|
||||||
// match on that to determine what fn to call
|
// match on that to determine what fn to call
|
||||||
match v.method.as_ref() {
|
match v.method.as_ref() {
|
||||||
"cryp_spawn" => {
|
"cryp_spawn" => {
|
||||||
match from_slice::<CrypSpawnMsg>(&data) {
|
match from_slice::<CrypSpawnMsg>(&data) {
|
||||||
Ok(v) => Ok(RpcResponse {
|
Ok(v) => {
|
||||||
method: v.method,
|
match user {
|
||||||
params: RpcResult::Cryp(spawn(v.params))
|
Some(u) => Ok(RpcResponse {
|
||||||
}),
|
method: v.method,
|
||||||
|
params: RpcResult::SpawnCryp(spawn(v.params, db, u)?)
|
||||||
|
}),
|
||||||
|
None => Err(err_msg("auth required")),
|
||||||
|
}
|
||||||
|
}
|
||||||
Err(_e) => Err(err_msg("invalid params")),
|
Err(_e) => Err(err_msg("invalid params")),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -67,7 +77,7 @@ pub struct RpcResponse {
|
|||||||
|
|
||||||
#[derive(Debug,Clone,Serialize,Deserialize)]
|
#[derive(Debug,Clone,Serialize,Deserialize)]
|
||||||
pub enum RpcResult {
|
pub enum RpcResult {
|
||||||
Cryp(Cryp),
|
SpawnCryp(Cryp),
|
||||||
User(User),
|
User(User),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,7 +95,7 @@ struct CrypSpawnMsg {
|
|||||||
|
|
||||||
#[derive(Debug,Clone,Serialize,Deserialize)]
|
#[derive(Debug,Clone,Serialize,Deserialize)]
|
||||||
pub struct CrypSpawnParams {
|
pub struct CrypSpawnParams {
|
||||||
pub level: u8,
|
pub name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug,Clone,Serialize,Deserialize)]
|
#[derive(Debug,Clone,Serialize,Deserialize)]
|
||||||
|
|||||||
@ -12,10 +12,12 @@ use rpc::{UserCreateParams, UserLoginParams, RpcResult};
|
|||||||
use failure::Error;
|
use failure::Error;
|
||||||
use failure::err_msg;
|
use failure::err_msg;
|
||||||
|
|
||||||
|
static PASSWORD_MIN_LEN: usize = 12;
|
||||||
|
|
||||||
#[derive(Debug,Clone,Serialize,Deserialize)]
|
#[derive(Debug,Clone,Serialize,Deserialize)]
|
||||||
pub struct User {
|
pub struct User {
|
||||||
id: Uuid,
|
pub id: Uuid,
|
||||||
name: String,
|
pub name: String,
|
||||||
token: String,
|
token: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,7 +29,31 @@ struct UserEntry {
|
|||||||
token: String,
|
token: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
static PASSWORD_MIN_LEN: usize = 12;
|
// MAYBE
|
||||||
|
// hash tokens with a secret
|
||||||
|
pub fn from_token(token: String, db: &Db) -> Result<User, Error> {
|
||||||
|
let query = "
|
||||||
|
SELECT id, name, token
|
||||||
|
FROM users
|
||||||
|
WHERE token = $1;
|
||||||
|
";
|
||||||
|
|
||||||
|
let result = db
|
||||||
|
.query(query, &[&token])?;
|
||||||
|
|
||||||
|
let returned = match result.iter().next() {
|
||||||
|
Some(row) => row,
|
||||||
|
None => return Err(err_msg("invalid token")),
|
||||||
|
};
|
||||||
|
|
||||||
|
let entry = User {
|
||||||
|
id: returned.get(0),
|
||||||
|
name: returned.get(1),
|
||||||
|
token: returned.get(2),
|
||||||
|
};
|
||||||
|
|
||||||
|
return Ok(entry);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn create(params: UserCreateParams, db: Db) -> Result<RpcResult, Error> {
|
pub fn create(params: UserCreateParams, db: Db) -> Result<RpcResult, Error> {
|
||||||
let id = Uuid::new_v4();
|
let id = Uuid::new_v4();
|
||||||
@ -73,7 +99,7 @@ pub fn create(params: UserCreateParams, db: Db) -> Result<RpcResult, Error> {
|
|||||||
|
|
||||||
tx.commit()?;
|
tx.commit()?;
|
||||||
|
|
||||||
Ok(RpcResult::User(entry))
|
return Ok(RpcResult::User(entry));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn login(params: UserLoginParams, db: Db) -> Result<RpcResult, Error> {
|
pub fn login(params: UserLoginParams, db: Db) -> Result<RpcResult, Error> {
|
||||||
@ -83,13 +109,13 @@ pub fn login(params: UserLoginParams, db: Db) -> Result<RpcResult, Error> {
|
|||||||
WHERE name = $1;
|
WHERE name = $1;
|
||||||
";
|
";
|
||||||
|
|
||||||
// let tx = db.transaction()?;
|
|
||||||
|
|
||||||
let result = db
|
let result = db
|
||||||
.query(query, &[¶ms.name])?;
|
.query(query, &[¶ms.name])?;
|
||||||
|
|
||||||
let returned = match result.iter().next() {
|
let returned = match result.iter().next() {
|
||||||
Some(row) => row,
|
Some(row) => row,
|
||||||
|
// MAYBE
|
||||||
|
// verify gibberish to delay response for timing attacks
|
||||||
None => return Err(err_msg("user not found")),
|
None => return Err(err_msg("user not found")),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -110,13 +136,11 @@ pub fn login(params: UserLoginParams, db: Db) -> Result<RpcResult, Error> {
|
|||||||
// update token?
|
// update token?
|
||||||
// don't necessarily want to log every session out when logging in
|
// don't necessarily want to log every session out when logging in
|
||||||
|
|
||||||
// tx.commit()?;
|
|
||||||
|
|
||||||
let user = User {
|
let user = User {
|
||||||
id: entry.id,
|
id: entry.id,
|
||||||
name: entry.name,
|
name: entry.name,
|
||||||
token: entry.token,
|
token: entry.token,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(RpcResult::User(user))
|
return Ok(RpcResult::User(user));
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user