multiple device authentication !
parent
51f4536424
commit
15acf67922
|
@ -115,7 +115,7 @@ function newThread() {
|
|||
document.getElementById('newthread').textContent = 'create';
|
||||
} else {
|
||||
window.threadmembers = [window.name];
|
||||
document.getElementById('threads').insertAdjacentElement('afterend', html.node`
|
||||
document.getElementById('home').insertAdjacentElement('afterend', html.node`
|
||||
<form id='createthread' class='column' onsubmit=${createThread}>
|
||||
<h3>create thread</h3>
|
||||
<label for='newthreadname' class='heading'>thread name</label>
|
||||
|
@ -147,8 +147,7 @@ function newThread() {
|
|||
<br />
|
||||
<button id='submitthread' type='submit'>create</button>
|
||||
</form>
|
||||
`
|
||||
);
|
||||
`);
|
||||
document.getElementById('newthread').textContent = 'cancel';
|
||||
}
|
||||
}
|
||||
|
@ -159,11 +158,20 @@ function clickedTab(event) {
|
|||
}
|
||||
|
||||
render(document.body, html`
|
||||
<div id='threads' class='column'>
|
||||
<h3>vybe</h3>
|
||||
<h4>threads</h4>
|
||||
<div id='threadlist'>loading...</div>
|
||||
<button id='newthread' onclick=${newThread}>create</button>
|
||||
<div id='home' class='column'>
|
||||
<div id='threads'>
|
||||
<h3>vybe</h3>
|
||||
<h4>threads</h4>
|
||||
<div id='threadlist'>loading...</div>
|
||||
<button id='newthread' onclick=${newThread}>create</button>
|
||||
</div>
|
||||
<div id='user' onclick=${() =>
|
||||
document.getElementById('profile').classList.toggle('hidden')
|
||||
}>${window.name}</div>
|
||||
</div>
|
||||
<div id='profile' class='column hidden'>
|
||||
<strong>authentication requests</strong>
|
||||
<div id='authrequests'></div>
|
||||
</div>
|
||||
<hr class='separator' color='#666'>
|
||||
<div id='thread' class='column'>
|
||||
|
@ -171,7 +179,8 @@ render(document.body, html`
|
|||
thread: <strong id='threadname'>meow</strong>
|
||||
</div>
|
||||
<div id='tabs'>
|
||||
<button id='messagetab' class='tab active' onclick=${clickedTab}>messages</button><button id='spacetab' class='tab' onclick=${clickedTab}>space</button>
|
||||
<button id='messagetab' class='tab active' onclick=${clickedTab}>messages
|
||||
</button><button id='spacetab' class='tab' onclick=${clickedTab}>space</button>
|
||||
</div>
|
||||
<div id='message' class='tabcontent'></div>
|
||||
<div id='space' class='tabcontent hidden'></div>
|
||||
|
@ -200,3 +209,16 @@ window.emit('list_threads', {}, msg => {
|
|||
threadlist.appendChild(makeThread(thread));
|
||||
chooseThread.call(threadlist.firstChild);
|
||||
});
|
||||
|
||||
window.socket.on('authrequest', (msg, respond) => {
|
||||
const div = html.node`
|
||||
<div class='authrequest'>
|
||||
<button onclick=${() => {
|
||||
respond(true);
|
||||
div.remove();
|
||||
}}>approve</button>
|
||||
session <strong>${msg.id}</strong>
|
||||
</div>`;
|
||||
document.getElementById('authrequests').append(div);
|
||||
setTimeout(() => div.remove(), Date.now() - msg.time + 60000 * 5);
|
||||
});
|
||||
|
|
|
@ -18,7 +18,11 @@ async function auth() {
|
|||
});
|
||||
window.socket.emit(
|
||||
'authenticate',
|
||||
{ name: window.name, message: sig },
|
||||
{
|
||||
name: window.name,
|
||||
message: sig,
|
||||
pubkey: window.keys.armored.publicKey
|
||||
},
|
||||
msg => {
|
||||
let register = document.getElementById('register');
|
||||
if (!msg.success) {
|
||||
|
@ -26,14 +30,46 @@ async function auth() {
|
|||
register.classList.remove('hidden');
|
||||
return;
|
||||
}
|
||||
if (register) {
|
||||
register.remove();
|
||||
import('/app.js');
|
||||
}
|
||||
localStorage.setItem('keys', JSON.stringify(window.keys.armored));
|
||||
localStorage.setItem('name', window.name);
|
||||
register.classList.add('hidden');
|
||||
import('/app.js');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async function submit(event) {
|
||||
event.preventDefault();
|
||||
const name = document.getElementById('name').value;
|
||||
if (!name)
|
||||
return;
|
||||
const keys = await openpgp.generateKey({
|
||||
userIDs: [{ name }]
|
||||
});
|
||||
const priv = await openpgp.readKey({ armoredKey: keys.privateKey });
|
||||
const pub = await openpgp.readKey({ armoredKey: keys.publicKey });
|
||||
window.keys = { priv, pub, armored: keys };
|
||||
window.name = name;
|
||||
if (this.id === 'registerform') {
|
||||
window.emit('create_user',
|
||||
{ name, pubkey: keys.publicKey },
|
||||
(msg) => {
|
||||
if (!msg.success) {
|
||||
document.getElementById('result').innerText = msg.message;
|
||||
return;
|
||||
}
|
||||
auth();
|
||||
}
|
||||
);
|
||||
}
|
||||
else {
|
||||
await auth();
|
||||
document.getElementById('result').innerHTML =
|
||||
`now open your profile on your other device to approve this authentication.
|
||||
this session's ID is <strong>${pub.getFingerprint().slice(0,8)}</strong>`;
|
||||
}
|
||||
}
|
||||
|
||||
render(document.body, html`
|
||||
<div id='register' class='hidden'>
|
||||
<h1>welcome to vybe</h1>
|
||||
|
@ -43,41 +79,21 @@ render(document.body, html`
|
|||
for security, rather than passwords. your keys are stored in your
|
||||
browser storage only, so do this on a browser you can access again.
|
||||
</p>
|
||||
<form onsubmit=${async e => {
|
||||
e.preventDefault();
|
||||
const name = document.getElementById('name').value;
|
||||
if (!name)
|
||||
return;
|
||||
const keys = await openpgp.generateKey({
|
||||
userIDs: [{ name }],
|
||||
});
|
||||
const priv = await openpgp.readKey({ armoredKey: keys.privateKey });
|
||||
const pub = await openpgp.readKey({ armoredKey: keys.publicKey });
|
||||
window.emit(
|
||||
'create_user',
|
||||
{ name, pubkey: keys.publicKey },
|
||||
(msg) => {
|
||||
if (!msg.success) {
|
||||
document.querySelector('#message').innerText = msg.message;
|
||||
return;
|
||||
}
|
||||
window.keys = { priv, pub };
|
||||
localStorage.setItem('keys', JSON.stringify(keys));
|
||||
localStorage.setItem('name', name);
|
||||
window.name = name;
|
||||
auth();
|
||||
}
|
||||
);
|
||||
}} id='registerform'>
|
||||
<p>
|
||||
if you already have an account, enter your username here,
|
||||
and you can authenticate using your other device.
|
||||
</p>
|
||||
<form onsubmit=${submit} id='registerform'>
|
||||
<label for='name'>username: </label>
|
||||
<input id='name' type='text' />
|
||||
<button id='submit' type='submit'>generate keys & register</button>
|
||||
<button id='submit' type='submit'>register</button>
|
||||
<button onclick=${submit}>authenticate</button>
|
||||
</form>
|
||||
<p id='message'></p>
|
||||
<p id='result'></p>
|
||||
</div>
|
||||
`);
|
||||
|
||||
async function gensession() {
|
||||
function gensession() {
|
||||
window.session = rand();
|
||||
|
||||
window.emit = (type, data, callback) =>
|
||||
|
@ -94,7 +110,7 @@ async function gensession() {
|
|||
window.onload = async () => {
|
||||
window.socket = io();
|
||||
|
||||
await gensession();
|
||||
gensession();
|
||||
|
||||
let keys = localStorage.getItem('keys');
|
||||
if (keys) {
|
||||
|
@ -102,7 +118,8 @@ window.onload = async () => {
|
|||
keys = JSON.parse(keys);
|
||||
window.keys = {
|
||||
priv: await openpgp.readKey({ armoredKey: keys.privateKey }),
|
||||
pub: await openpgp.readKey({ armoredKey: keys.publicKey })
|
||||
pub: await openpgp.readKey({ armoredKey: keys.publicKey }),
|
||||
armored: keys
|
||||
};
|
||||
await auth();
|
||||
}
|
||||
|
@ -110,8 +127,8 @@ window.onload = async () => {
|
|||
document.getElementById('register').classList.remove('hidden');
|
||||
|
||||
window.socket.io.on('reconnect', async attempt => {
|
||||
await gensession();
|
||||
if (localStorage.getItem('keys'))
|
||||
gensession();
|
||||
if (window.keys)
|
||||
await auth();
|
||||
});
|
||||
};
|
||||
|
|
|
@ -54,20 +54,21 @@
|
|||
background: #1b1b1b;
|
||||
outline: none;
|
||||
border: 1px solid #444;
|
||||
}
|
||||
input:focus {
|
||||
padding-bottom: 3px;
|
||||
border-bottom: 3px solid #777;
|
||||
}
|
||||
input::placeholder {
|
||||
color: #aaa;
|
||||
&:focus {
|
||||
padding-bottom: 3px;
|
||||
border-bottom: 3px solid #777;
|
||||
}
|
||||
&::placeholder {
|
||||
color: #aaa;
|
||||
}
|
||||
}
|
||||
#register {
|
||||
margin-inline: 14px;
|
||||
max-width: 800px;
|
||||
}
|
||||
.thread:hover,
|
||||
.tab:hover {
|
||||
.tab:hover,
|
||||
#user:hover {
|
||||
background-color: #303030;
|
||||
}
|
||||
.tab.active,
|
||||
|
@ -90,13 +91,32 @@
|
|||
.column {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
margin: 5px;
|
||||
}
|
||||
.separator {
|
||||
margin: 8px 2px;
|
||||
}
|
||||
#threads {
|
||||
#home {
|
||||
max-width: 250px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
#threads {
|
||||
margin: 5px;
|
||||
}
|
||||
#user {
|
||||
margin: 3px;
|
||||
padding: 6px;
|
||||
background-color: #191919;
|
||||
}
|
||||
#profile {
|
||||
max-width: 250px;
|
||||
> * {
|
||||
margin: 5px;
|
||||
}
|
||||
}
|
||||
.authrequest {
|
||||
margin-block: 3px;
|
||||
}
|
||||
.thread {
|
||||
padding: 2px 4px;
|
||||
|
@ -113,7 +133,6 @@
|
|||
#thread {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 0;
|
||||
}
|
||||
#title {
|
||||
margin: 4px;
|
||||
|
@ -143,14 +162,12 @@
|
|||
#msginput {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin: 2px;
|
||||
> * {
|
||||
margin: 3px;
|
||||
}
|
||||
}
|
||||
#msg {
|
||||
flex-grow: 1;
|
||||
margin: 2px;
|
||||
}
|
||||
#sendmsg {
|
||||
margin: 2px 3px;
|
||||
}
|
||||
.message {
|
||||
margin-bottom: 5px;
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
create table user (
|
||||
id integer primary key asc,
|
||||
name text,
|
||||
pubkey text,
|
||||
created timestamp default current_timestamp
|
||||
);
|
||||
|
||||
create table authentication (
|
||||
create table key (
|
||||
user integer,
|
||||
salt text,
|
||||
pubkey text,
|
||||
created timestamp default current_timestamp,
|
||||
active boolean,
|
||||
foreign key(user) references user(id)
|
||||
);
|
||||
|
||||
|
@ -34,7 +34,6 @@ create table permission (
|
|||
create table member (
|
||||
thread integer,
|
||||
user integer,
|
||||
key_delivery text,
|
||||
created timestamp default current_timestamp,
|
||||
foreign key(user) references user(id),
|
||||
foreign key(thread) references thread(id)
|
||||
|
|
22
index.js
22
index.js
|
@ -20,21 +20,27 @@ const io = new Server(server, {
|
|||
},
|
||||
});
|
||||
|
||||
io.cache = {};
|
||||
|
||||
io.on('connection', (socket) => {
|
||||
for (let event in events) {
|
||||
socket.on(event, (msg, callback) =>
|
||||
events[event](msg, callback, socket, io)
|
||||
);
|
||||
socket.on(event, (msg, callback) => {
|
||||
if (!events[event]) {
|
||||
callback('no such event ' + event);
|
||||
return;
|
||||
}
|
||||
events[event](msg, callback, socket);
|
||||
});
|
||||
}
|
||||
socket.on('disconnect', reason => {
|
||||
let sockets = io.cache[socket.username];
|
||||
if (sockets)
|
||||
sockets.splice(sockets.indexOf(socket.id), 1);
|
||||
let user = vybe.users[socket.username];
|
||||
if (user)
|
||||
user.sockets.splice(user.sockets.indexOf(socket), 1);
|
||||
})
|
||||
});
|
||||
|
||||
global.vybe = {
|
||||
users: {}
|
||||
};
|
||||
|
||||
server.listen(PORT, () => {
|
||||
console.log('server running on port ' + PORT);
|
||||
});
|
||||
|
|
|
@ -1,27 +1,21 @@
|
|||
const db = require('./db');
|
||||
|
||||
const authwrap = (fn) => async (msg, respond, socket, io) => {
|
||||
const authwrap = (fn) => async (msg, respond, socket) => {
|
||||
if (!respond)
|
||||
respond = () => {};
|
||||
if (!msg || !msg.__session) {
|
||||
if (!msg || !msg.__session || socket.auth !== msg.__session) {
|
||||
return respond({
|
||||
success: false,
|
||||
message: 'not authenticated',
|
||||
message: 'not authenticated'
|
||||
});
|
||||
}
|
||||
const result = await db.query(
|
||||
`select user.* from user join authentication
|
||||
on authentication.user = user.id
|
||||
where authentication.salt = ?`,
|
||||
[msg.__session]
|
||||
);
|
||||
if (result.rows.length === 0) {
|
||||
return respond({
|
||||
success: false,
|
||||
message: 'user not found',
|
||||
});
|
||||
}
|
||||
return await fn({ ...msg, auth_user: result.rows[0] }, respond, socket, io);
|
||||
return await fn({
|
||||
...msg,
|
||||
auth_user: {
|
||||
id: vybe.users[socket.username].id,
|
||||
name: socket.username
|
||||
}
|
||||
}, respond, socket);
|
||||
};
|
||||
|
||||
module.exports = authwrap;
|
||||
|
|
|
@ -1,27 +1,32 @@
|
|||
const db = require('../db');
|
||||
const openpgp = require('openpgp');
|
||||
|
||||
const authenticate = async (msg, respond, socket, io) => {
|
||||
const authenticate = async (msg, respond, socket) => {
|
||||
if (!msg.name || !msg.message) {
|
||||
return respond({
|
||||
success: false,
|
||||
message: 'invalid message'
|
||||
});
|
||||
}
|
||||
const result = await db.query('select * from user where name = ?', [
|
||||
msg.name
|
||||
]);
|
||||
if (result.rows.length === 0) {
|
||||
let userid = await db.query(
|
||||
`select user.id from user where name = ?`, [msg.name]);
|
||||
if (userid.rows.length === 0) {
|
||||
return respond({
|
||||
success: false,
|
||||
message: 'user not found'
|
||||
});
|
||||
}
|
||||
let result = await db.query(
|
||||
`select user.id from user
|
||||
join key on key.user = user.id
|
||||
where name = ? and pubkey = ? and active = true`,
|
||||
[msg.name, msg.pubkey]
|
||||
);
|
||||
try {
|
||||
const key = await openpgp.readKey({ armoredKey: result.rows[0].pubkey });
|
||||
const key = await openpgp.readKey({ armoredKey: msg.pubkey });
|
||||
const verification = await openpgp.verify({
|
||||
message: await openpgp.readCleartextMessage({
|
||||
cleartextMessage: msg.message,
|
||||
cleartextMessage: msg.message
|
||||
}),
|
||||
verificationKeys: key,
|
||||
expectSigned: true
|
||||
|
@ -33,31 +38,52 @@ const authenticate = async (msg, respond, socket, io) => {
|
|||
message: 'bad auth message'
|
||||
});
|
||||
}
|
||||
const auths = await db.query(
|
||||
'select * from authentication where user = ? and salt = ?',
|
||||
[result.rows[0].id, data[1]]
|
||||
);
|
||||
if (auths.rows.length === 0) {
|
||||
await db.query('insert into authentication (user, salt) values (?, ?)', [
|
||||
result.rows[0].id,
|
||||
data[1]
|
||||
]);
|
||||
socket.username = msg.name;
|
||||
if (io.cache[msg.name]) {
|
||||
io.cache[msg.name].push(socket.id);
|
||||
} else {
|
||||
io.cache[msg.name] = [socket.id];
|
||||
}
|
||||
return respond({
|
||||
success: true,
|
||||
});
|
||||
} else {
|
||||
return respond({
|
||||
success: false,
|
||||
message: 'already authenticated with this message'
|
||||
});
|
||||
let user = vybe.users[msg.name];
|
||||
if (!user)
|
||||
user = vybe.users[msg.name] = {
|
||||
id: userid.rows[0].id,
|
||||
sockets: [],
|
||||
authrequests: {}
|
||||
};
|
||||
if (result.rows.length === 0) {
|
||||
// request auth from logged in sessions
|
||||
let id = key.getFingerprint().slice(0, 8);
|
||||
let time = Date.now();
|
||||
if (!await new Promise(resolve => {
|
||||
user.authrequests[id] = { time, callback: resolve };
|
||||
for (let s of user.sockets)
|
||||
s.emit('authrequest', { id, time }, resolve);
|
||||
setTimeout(() => {
|
||||
delete user.authrequests[id];
|
||||
resolve(false);
|
||||
}, 60000 * 5);
|
||||
}))
|
||||
return;
|
||||
delete user.authrequests[id];
|
||||
if (Date.now() - time > 60000 * 5)
|
||||
return;
|
||||
await db.query(
|
||||
'insert into key (user, pubkey, active) values (?, ?, true)',
|
||||
[user.id, msg.pubkey]);
|
||||
}
|
||||
} catch (err) {
|
||||
// this socket is now authenticated
|
||||
socket.auth = data[1];
|
||||
socket.username = msg.name;
|
||||
user.sockets.push(socket);
|
||||
respond({
|
||||
success: true
|
||||
});
|
||||
// send authenticated session any auth requests
|
||||
if (result.rows.length)
|
||||
setTimeout(() => {
|
||||
for (let id in user.authrequests)
|
||||
socket.emit('authrequest', {
|
||||
id,
|
||||
time: user.authrequests[id].time
|
||||
}, user.authrequests[id].callback);
|
||||
}, 1000); // todo: do this better
|
||||
}
|
||||
catch (err) {
|
||||
console.error('error in authentication: ' + err);
|
||||
return respond({
|
||||
success: false,
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
const db = require('../db');
|
||||
const authwrap = require('../authwrap');
|
||||
|
||||
const create_thread = async (msg, respond, socket, io) => {
|
||||
const create_thread = async (msg, respond, socket) => {
|
||||
// validate inputs
|
||||
if (typeof msg.name !== 'string') {
|
||||
return respond({
|
||||
success: false,
|
||||
message: 'thread name required',
|
||||
message: 'thread name required'
|
||||
});
|
||||
}
|
||||
if (msg.name.length > 200) {
|
||||
return respond({
|
||||
success: false,
|
||||
message: 'thread name 200 chars max',
|
||||
message: 'thread name 200 chars max'
|
||||
});
|
||||
}
|
||||
// add to db
|
||||
|
@ -63,38 +63,37 @@ const create_thread = async (msg, respond, socket, io) => {
|
|||
if (id.rows.length > 0) {
|
||||
const user_id = id.rows[0].id;
|
||||
await db.query(
|
||||
'insert into member (thread, user, key_delivery) values (?, ?, ?)',
|
||||
[thread_id, user_id, user.key]
|
||||
'insert into member (thread, user) values (?, ?)',
|
||||
[thread_id, user_id]
|
||||
);
|
||||
}
|
||||
}
|
||||
if (!msg.permissions || !msg.permissions.view_limited) {
|
||||
for (let username in io.cache) {
|
||||
for (let socket of io.cache[username]) {
|
||||
io.to(socket).emit('new_thread', {
|
||||
for (let username in vybe.users) {
|
||||
for (let socket of vybe.users[username].sockets) {
|
||||
socket.emit('new_thread', {
|
||||
name: msg.name,
|
||||
id: insert.rows[0].id,
|
||||
permissions: {
|
||||
is_member: false,
|
||||
view: true,
|
||||
post: !msg.permissions || !msg.permissions.post_limited
|
||||
},
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (let member of msg.members) {
|
||||
for (let socket of io.cache[member.name]) {
|
||||
io.to(socket).emit('new_thread', {
|
||||
for (let socket of vybe.users[member.name].sockets) {
|
||||
socket.emit('new_thread', {
|
||||
name: msg.name,
|
||||
id: insert.rows[0].id,
|
||||
permissions: {
|
||||
is_member: true,
|
||||
view: true,
|
||||
post: true
|
||||
},
|
||||
key: member.key
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -102,7 +101,7 @@ const create_thread = async (msg, respond, socket, io) => {
|
|||
// respond
|
||||
return respond({
|
||||
success: true,
|
||||
id: insert.rows[0].id,
|
||||
id: insert.rows[0].id
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -6,45 +6,49 @@ const create_user = async (msg, respond) => {
|
|||
if (!msg.name) {
|
||||
return respond({
|
||||
success: false,
|
||||
message: 'username required',
|
||||
message: 'username required'
|
||||
});
|
||||
}
|
||||
if (!msg.pubkey) {
|
||||
return respond({
|
||||
success: false,
|
||||
message: 'public key required'
|
||||
});
|
||||
}
|
||||
// ensure username is not taken
|
||||
const result = await db.query('select * from user where name = ?', [
|
||||
msg.name,
|
||||
msg.name
|
||||
]);
|
||||
if (result.rows.length > 0) {
|
||||
console.log(`username already exists: ${result}`);
|
||||
return respond({
|
||||
success: false,
|
||||
message: 'a user with this name already exists on this server',
|
||||
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) {
|
||||
console.err('error in create_user readkey: ' + err);
|
||||
return respond({
|
||||
success: false,
|
||||
message: 'public key invalid',
|
||||
message: 'public key invalid'
|
||||
});
|
||||
}
|
||||
// add to db
|
||||
const insert = await db.query(
|
||||
'insert into user (name, pubkey) values (?, ?) returning id',
|
||||
[msg.name, msg.pubkey]
|
||||
'insert into user (name) values (?) returning id',
|
||||
[msg.name]
|
||||
);
|
||||
await db.query(
|
||||
'insert into key (user, pubkey, active) values (?, ?, true)',
|
||||
[insert.rows[0].id, msg.pubkey]
|
||||
)
|
||||
// respond
|
||||
return respond({
|
||||
success: true,
|
||||
id: insert.rows[0].id,
|
||||
id: insert.rows[0].id
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -1,30 +1,29 @@
|
|||
const db = require('../db');
|
||||
const authwrap = require('../authwrap');
|
||||
|
||||
const get_keys = async (msg, respond, socket, io) => {
|
||||
const get_keys = async (msg, respond) => {
|
||||
// validate inputs
|
||||
if (!msg.names) {
|
||||
return respond({
|
||||
success: false,
|
||||
message: 'user names required',
|
||||
message: 'user names required'
|
||||
});
|
||||
}
|
||||
if (typeof msg.names !== 'object') {
|
||||
return respond({
|
||||
success: false,
|
||||
message: "can't iterate user names",
|
||||
message: "can't iterate user names"
|
||||
});
|
||||
}
|
||||
const keys = await db.query(
|
||||
`select name, pubkey from user where name in (${msg.names
|
||||
.map((i) => '?')
|
||||
.join(',')})`,
|
||||
`select name, pubkey from user where name in
|
||||
(${msg.names.map((i) => '?').join(',')})`,
|
||||
msg.names
|
||||
);
|
||||
// respond
|
||||
return respond({
|
||||
success: true,
|
||||
keys: keys.rows,
|
||||
keys: keys.rows
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -6,17 +6,17 @@ const get_space = async (msg, respond) => {
|
|||
if (!msg.thread) {
|
||||
return respond({
|
||||
success: false,
|
||||
message: 'thread ID required',
|
||||
message: 'thread ID required'
|
||||
});
|
||||
}
|
||||
if (!(await check_permission(msg.auth_user.id, msg.thread)).view) {
|
||||
return respond({
|
||||
success: false,
|
||||
message: "you can't view this thread",
|
||||
message: "you can't view this thread"
|
||||
});
|
||||
}
|
||||
const spans = await db.query(
|
||||
'select id, content, x, y, scale from span where thread=? and deleted=false',
|
||||
'select id, content, x, y, scale from span where thread = ? and deleted = false',
|
||||
[msg.thread]
|
||||
);
|
||||
return respond({
|
||||
|
|
|
@ -4,7 +4,7 @@ const check_permission = require('../check_permission');
|
|||
|
||||
const list_threads = async (msg, respond) => {
|
||||
const threads = await db.query(
|
||||
`select name, id, member.key_delivery as key from thread
|
||||
`select name, id from thread
|
||||
join permission on thread.id = permission.thread
|
||||
left join member on thread.id = member.thread
|
||||
where permission.permission = 'view'
|
||||
|
@ -20,12 +20,12 @@ const list_threads = async (msg, respond) => {
|
|||
for (let thread of threads.rows) {
|
||||
rows.push({
|
||||
...thread,
|
||||
permissions: await check_permission(msg.auth_user.id, thread.id),
|
||||
permissions: await check_permission(msg.auth_user.id, thread.id)
|
||||
});
|
||||
}
|
||||
return respond({
|
||||
success: true,
|
||||
threads: rows,
|
||||
threads: rows
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ const db = require('../db');
|
|||
const authwrap = require('../authwrap');
|
||||
const check_permission = require('../check_permission');
|
||||
|
||||
const save_span = async (msg, respond, socket, io) => {
|
||||
const save_span = async (msg, respond, socket) => {
|
||||
if (!msg.thread) {
|
||||
return respond({
|
||||
success: false,
|
||||
|
@ -43,12 +43,11 @@ const save_span = async (msg, respond, socket, io) => {
|
|||
"select * from permission where thread = ? and type = 'everyone' and value = 'true' and permission = 'view'",
|
||||
[msg.thread]
|
||||
);
|
||||
for (let username in io.cache) {
|
||||
for (let username in vybe.users) {
|
||||
if (permissions.rows.length > 0 || members.includes(username)) {
|
||||
const sockets = io.cache[username];
|
||||
for (let s of sockets) {
|
||||
if (s !== socket.id)
|
||||
io.to(s).emit('span', {
|
||||
for (let s of vybe.users[username].sockets) {
|
||||
if (s !== socket)
|
||||
s.emit('span', {
|
||||
id,
|
||||
thread: msg.thread,
|
||||
content: msg.content,
|
||||
|
|
|
@ -2,17 +2,17 @@ const db = require('../db');
|
|||
const authwrap = require('../authwrap');
|
||||
const check_permission = require('../check_permission');
|
||||
|
||||
const send_message = async (msg, respond, socket, io) => {
|
||||
const send_message = async (msg, respond) => {
|
||||
if (!msg.thread) {
|
||||
return respond({
|
||||
success: false,
|
||||
message: 'thread ID required',
|
||||
message: 'thread ID required'
|
||||
});
|
||||
}
|
||||
if (!(await check_permission(msg.auth_user.id, msg.thread)).post) {
|
||||
return respond({
|
||||
success: false,
|
||||
message: "you can't post to this thread",
|
||||
message: "you can't post to this thread"
|
||||
});
|
||||
}
|
||||
// add message and send it to everyone
|
||||
|
@ -32,22 +32,21 @@ const send_message = async (msg, respond, socket, io) => {
|
|||
"select * from permission where thread = ? and type = 'everyone' and value = 'true' and permission = 'view'",
|
||||
[msg.thread]
|
||||
);
|
||||
for (let username in io.cache) {
|
||||
for (let username in vybe.users) {
|
||||
if (permissions.rows.length > 0 || members.includes(username)) {
|
||||
const sockets = io.cache[username];
|
||||
for (let s of sockets) {
|
||||
io.to(s).emit('new_message', {
|
||||
for (let s of vybe.users[username].sockets) {
|
||||
s.emit('new_message', {
|
||||
id: id.rows[0].id,
|
||||
name: msg.auth_user.name,
|
||||
message: msg.message,
|
||||
thread: msg.thread,
|
||||
thread: msg.thread
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return respond({
|
||||
success: true,
|
||||
id: id.rows[0].id,
|
||||
id: id.rows[0].id
|
||||
});
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue