permissions (basic)
parent
85d5297c94
commit
89f8032034
7
DOCS.md
7
DOCS.md
|
@ -90,7 +90,12 @@ Message format:
|
|||
|
||||
```json
|
||||
{
|
||||
"name": "thread name"
|
||||
"name": "thread name",
|
||||
"permissions": {
|
||||
"view_limited": true,
|
||||
"post_limited": true
|
||||
},
|
||||
"members": ["username1", "username2"]
|
||||
}
|
||||
```
|
||||
|
||||
|
|
335
client/chat.js
335
client/chat.js
|
@ -1,161 +1,218 @@
|
|||
function rand() {
|
||||
let str = "";
|
||||
const lookups =
|
||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".split("");
|
||||
while (str.length < 16) {
|
||||
const n = Math.random() * lookups.length;
|
||||
str += lookups[Math.floor(n)];
|
||||
}
|
||||
return str;
|
||||
let str = "";
|
||||
const lookups =
|
||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".split("");
|
||||
while (str.length < 16) {
|
||||
const n = Math.random() * lookups.length;
|
||||
str += lookups[Math.floor(n)];
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
async function auth() {
|
||||
let session = rand();
|
||||
const sig = await openpgp.sign({
|
||||
message: new openpgp.CleartextMessage("vybe_auth " + session, ""),
|
||||
signingKeys: window.keys.priv,
|
||||
});
|
||||
window.session = session;
|
||||
window.socket.emit("authenticate", { name: window.name, message: sig });
|
||||
let session = rand();
|
||||
const sig = await openpgp.sign({
|
||||
message: new openpgp.CleartextMessage("vybe_auth " + session, ""),
|
||||
signingKeys: window.keys.priv,
|
||||
});
|
||||
window.session = session;
|
||||
window.socket.emit("authenticate", { name: window.name, message: sig });
|
||||
}
|
||||
|
||||
async function loadKeys(keys) {
|
||||
const priv = await openpgp.readKey({ armoredKey: keys.privateKey });
|
||||
const pub = await openpgp.readKey({ armoredKey: keys.publicKey });
|
||||
window.keys = { priv, pub };
|
||||
await auth();
|
||||
const priv = await openpgp.readKey({ armoredKey: keys.privateKey });
|
||||
const pub = await openpgp.readKey({ armoredKey: keys.publicKey });
|
||||
window.keys = { priv, pub };
|
||||
await auth();
|
||||
}
|
||||
|
||||
function chooseThread(thread) {
|
||||
window.currentThreadId = thread.id;
|
||||
window.earliestMessage = null;
|
||||
document.getElementById("messages").innerHTML = "";
|
||||
document.getElementById("threadname").innerHTML = thread.name;
|
||||
window.currentThreadId = thread.id;
|
||||
window.earliestMessage = null;
|
||||
document.getElementById("messages").innerHTML = "";
|
||||
document.getElementById("threadname").innerHTML = thread.name;
|
||||
}
|
||||
|
||||
function loadMessages() {
|
||||
window.socket.emit(
|
||||
"get_history",
|
||||
{ before: window.earliestMessage, thread: window.currentThreadId }
|
||||
);
|
||||
window.socket.emit("get_history", {
|
||||
before: window.earliestMessage,
|
||||
thread: window.currentThreadId,
|
||||
});
|
||||
}
|
||||
|
||||
function addThread(thread) {
|
||||
const el = document.createElement("div");
|
||||
el.classList.add("thread");
|
||||
el.innerHTML = thread.name;
|
||||
const btn = document.createElement("button");
|
||||
btn.innerHTML = "choose";
|
||||
btn.onclick = () => {
|
||||
chooseThread(thread);
|
||||
loadMessages();
|
||||
document.getElementById("loadmore").classList.remove("hidden");
|
||||
};
|
||||
el.appendChild(btn);
|
||||
document.getElementById("threadlist").appendChild(el);
|
||||
const el = document.createElement("div");
|
||||
el.classList.add("thread");
|
||||
el.innerHTML = thread.name;
|
||||
const btn = document.createElement("button");
|
||||
btn.innerHTML = "choose";
|
||||
btn.onclick = () => {
|
||||
chooseThread(thread);
|
||||
loadMessages();
|
||||
document.getElementById("loadmore").classList.remove("hidden");
|
||||
if (!thread.permissions.post) {
|
||||
document.getElementById("msginput").classList.add("hidden");
|
||||
} else {
|
||||
document.getElementById("msginput").classList.remove("hidden");
|
||||
}
|
||||
};
|
||||
el.appendChild(btn);
|
||||
document.getElementById("threadlist").appendChild(el);
|
||||
}
|
||||
|
||||
function addMember() {
|
||||
const name = document.getElementById("membername").value;
|
||||
if (!window.threadmembers) {
|
||||
window.threadmembers = [window.name, name];
|
||||
} else {
|
||||
window.threadmembers.push(name);
|
||||
}
|
||||
const member = document.createElement("p");
|
||||
member.textContent = name;
|
||||
member.classList.add("member");
|
||||
document.getElementById("memberlist").appendChild(member);
|
||||
document.getElementById("membername").value = "";
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
window.currentThreadId = 1;
|
||||
window.socket = io();
|
||||
window.socket.on("create_user", auth);
|
||||
window.socket.on("new_message", (msg) => {
|
||||
if (msg.thread !== window.currentThreadId) return;
|
||||
const el = document.createElement("div");
|
||||
el.classList.add("message");
|
||||
const strong = document.createElement('strong');
|
||||
strong.textContent = msg.name + ': ';
|
||||
el.append(strong, msg.message);
|
||||
document.getElementById("messages").appendChild(el);
|
||||
if (!window.earliestMessage)
|
||||
window.earliestMessage = msg.id;
|
||||
});
|
||||
window.socket.on("get_history", (msg) => {
|
||||
if (msg.messages.length > 0) {
|
||||
window.earliestMessage = msg.messages[msg.messages.length - 1].id;
|
||||
for (let message of msg.messages) {
|
||||
const el = document.createElement("div");
|
||||
el.classList.add("message");
|
||||
const strong = document.createElement('strong');
|
||||
strong.textContent = message.name + ': ';
|
||||
el.append(strong, message.message);
|
||||
document.getElementById("messages").prepend(el);
|
||||
}
|
||||
}
|
||||
if (!msg.more)
|
||||
document.getElementById("loadmore").classList.add("hidden");
|
||||
});
|
||||
window.socket.on("authenticate", (msg) => {
|
||||
if (msg.success) {
|
||||
document.getElementById("register").classList.add("hidden");
|
||||
document.getElementById("threads").classList.remove("hidden");
|
||||
document.getElementById("chat").classList.remove("hidden");
|
||||
window.socket.emit("list_threads");
|
||||
}
|
||||
let emitter = window.socket.emit;
|
||||
window.socket.emit = (type, data) => {
|
||||
if (data)
|
||||
return emitter.call(window.socket, type, {
|
||||
...data,
|
||||
__session: window.session,
|
||||
});
|
||||
else return emitter.call(window.socket, type);
|
||||
};
|
||||
});
|
||||
window.socket.on("list_threads", (msg) => {
|
||||
document.getElementById("threadlist").innerHTML = "";
|
||||
for (let thread of msg.threads)
|
||||
addThread(thread);
|
||||
});
|
||||
window.socket.on('new_thread', addThread);
|
||||
window.socket.on("create_thread", (msg) => {
|
||||
chooseThread({
|
||||
name: document.getElementById("newthreadname").value,
|
||||
id: msg.id,
|
||||
});
|
||||
document.getElementById("newthreadname").value = "";
|
||||
});
|
||||
window.currentThreadId = 1;
|
||||
window.socket = io();
|
||||
window.socket.on("create_user", auth);
|
||||
window.socket.on("new_message", (msg) => {
|
||||
if (msg.thread !== window.currentThreadId) return;
|
||||
const el = document.createElement("div");
|
||||
el.classList.add("message");
|
||||
const strong = document.createElement("strong");
|
||||
strong.textContent = msg.name + ": ";
|
||||
el.append(strong, msg.message);
|
||||
document.getElementById("messages").appendChild(el);
|
||||
if (!window.earliestMessage) window.earliestMessage = msg.id;
|
||||
});
|
||||
window.socket.on("get_history", (msg) => {
|
||||
if (msg.messages.length > 0) {
|
||||
window.earliestMessage = msg.messages[msg.messages.length - 1].id;
|
||||
for (let message of msg.messages) {
|
||||
const el = document.createElement("div");
|
||||
el.classList.add("message");
|
||||
const strong = document.createElement("strong");
|
||||
strong.textContent = message.name + ": ";
|
||||
el.append(strong, message.message);
|
||||
document.getElementById("messages").prepend(el);
|
||||
}
|
||||
}
|
||||
if (!msg.more) document.getElementById("loadmore").classList.add("hidden");
|
||||
});
|
||||
window.socket.on("authenticate", (msg) => {
|
||||
if (msg.success) {
|
||||
document.getElementById("register").classList.add("hidden");
|
||||
document.getElementById("threads").classList.remove("hidden");
|
||||
document.getElementById("chat").classList.remove("hidden");
|
||||
const member = document.createElement("p");
|
||||
member.textContent = window.name;
|
||||
member.classList.add("member");
|
||||
document.getElementById("memberlist").appendChild(member);
|
||||
let emitter = window.socket.emit;
|
||||
window.socket.emit = (type, data) => {
|
||||
if (data)
|
||||
return emitter.call(window.socket, type, {
|
||||
...data,
|
||||
__session: window.session,
|
||||
});
|
||||
else return emitter.call(window.socket, type);
|
||||
};
|
||||
window.socket.emit("list_threads", {});
|
||||
} else {
|
||||
document.getElementById("register").classList.remove("hidden");
|
||||
}
|
||||
});
|
||||
window.socket.on("list_threads", (msg) => {
|
||||
document.getElementById("threadlist").innerHTML = "";
|
||||
for (let thread of msg.threads) addThread(thread);
|
||||
});
|
||||
window.socket.on("new_thread", addThread);
|
||||
window.socket.on("create_thread", (msg) => {
|
||||
chooseThread({
|
||||
name: document.getElementById("newthreadname").value,
|
||||
id: msg.id,
|
||||
});
|
||||
document.getElementById("newthreadname").value = "";
|
||||
document.getElementById("loadmore").classList.add("hidden");
|
||||
document.getElementById("msginput").classList.remove("hidden");
|
||||
});
|
||||
|
||||
document.getElementById("registerform").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.keys = { priv, pub };
|
||||
localStorage.setItem("keys", JSON.stringify(keys));
|
||||
localStorage.setItem("name", name);
|
||||
window.name = name;
|
||||
window.socket.emit("create_user", { name, pubkey: keys.publicKey });
|
||||
};
|
||||
document.getElementById("msginput").onsubmit = e => {
|
||||
e.preventDefault();
|
||||
const msg = document.getElementById("msg").value;
|
||||
if (!msg) return;
|
||||
window.socket.emit("send_message", {
|
||||
message: msg,
|
||||
thread: window.currentThreadId,
|
||||
});
|
||||
document.getElementById("msg").value = "";
|
||||
};
|
||||
document.getElementById("loadmore").onclick = e => {
|
||||
loadMessages();
|
||||
};
|
||||
document.getElementById("createthread").onsubmit = e => {
|
||||
e.preventDefault();
|
||||
window.socket.emit("create_thread", {
|
||||
name: document.getElementById("newthreadname").value,
|
||||
});
|
||||
};
|
||||
document.getElementById("registerform").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.keys = { priv, pub };
|
||||
localStorage.setItem("keys", JSON.stringify(keys));
|
||||
localStorage.setItem("name", name);
|
||||
window.name = name;
|
||||
window.socket.emit("create_user", { name, pubkey: keys.publicKey });
|
||||
};
|
||||
document.getElementById("msginput").onsubmit = (e) => {
|
||||
e.preventDefault();
|
||||
const msg = document.getElementById("msg").value;
|
||||
if (!msg) return;
|
||||
window.socket.emit("send_message", {
|
||||
message: msg,
|
||||
thread: window.currentThreadId,
|
||||
});
|
||||
document.getElementById("msg").value = "";
|
||||
};
|
||||
document.getElementById("loadmore").onclick = (e) => {
|
||||
loadMessages();
|
||||
};
|
||||
document.getElementById("createthread").onsubmit = (e) => {
|
||||
e.preventDefault();
|
||||
const perms = document.querySelector(
|
||||
'input[name="permissions"]:checked'
|
||||
).value;
|
||||
let permissions;
|
||||
if (perms === "public") {
|
||||
permissions = {
|
||||
view_limited: false,
|
||||
post_limited: false,
|
||||
};
|
||||
} else if (perms === "private_post") {
|
||||
permissions = {
|
||||
view_limited: false,
|
||||
post_limited: true,
|
||||
};
|
||||
} else if (perms === "private_view") {
|
||||
permissions = {
|
||||
view_limited: true,
|
||||
post_limited: true,
|
||||
};
|
||||
}
|
||||
window.socket.emit("create_thread", {
|
||||
name: document.getElementById("newthreadname").value,
|
||||
permissions,
|
||||
members: window.threadmembers || [window.name],
|
||||
});
|
||||
document.getElementById(perms).checked = false;
|
||||
window.threadmembers = null;
|
||||
document.getElementById("memberlist").innerHTML = "";
|
||||
const member = document.createElement("p");
|
||||
member.textContent = window.name;
|
||||
member.classList.add("member");
|
||||
document.getElementById("memberlist").appendChild(member);
|
||||
};
|
||||
document.getElementById("membername").onkeydown = (e) => {
|
||||
if (e.key == "Enter") {
|
||||
addMember();
|
||||
}
|
||||
};
|
||||
document.getElementById("addmember").onclick = addMember;
|
||||
|
||||
const keys = localStorage.getItem("keys");
|
||||
if (keys) {
|
||||
window.name = localStorage.getItem("name");
|
||||
loadKeys(JSON.parse(keys)).then(() => {});
|
||||
}
|
||||
else
|
||||
document.getElementById("register").classList.remove("hidden");
|
||||
const keys = localStorage.getItem("keys");
|
||||
if (keys) {
|
||||
window.name = localStorage.getItem("name");
|
||||
loadKeys(JSON.parse(keys)).then(() => {});
|
||||
} else document.getElementById("register").classList.remove("hidden");
|
||||
};
|
||||
|
|
|
@ -1,89 +1,125 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<script src="/openpgp.min.js"></script>
|
||||
<script src="/socket.io.min.v4.6.1.js"></script>
|
||||
<script src="/chat.js"></script>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>vybe</title>
|
||||
<style>
|
||||
* {
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
|
||||
Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue",
|
||||
sans-serif;
|
||||
}
|
||||
body, button, input {
|
||||
background: #020202;
|
||||
color: #eaeaea;
|
||||
}
|
||||
body {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
margin: 0;
|
||||
min-width: min-content;
|
||||
}
|
||||
.column {
|
||||
flex-grow: 1;
|
||||
}
|
||||
button {
|
||||
border-color: #767676;
|
||||
}
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
#msginput {
|
||||
margin-top: 15px;
|
||||
}
|
||||
.message {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
#loadmore {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.channel {
|
||||
font-weight: normal;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="register" class="hidden">
|
||||
<h1>welcome to vybe</h1>
|
||||
<h3>a communication network (beta)</h3>
|
||||
<p>
|
||||
to get started, you'll need an account. we use public key
|
||||
cryptography for security, rather than passwords. for now
|
||||
your keys are stored in your browser storage only.
|
||||
</p>
|
||||
<form id="registerform">
|
||||
<label for="name">name/username: </label>
|
||||
<input type="text" id="name" />
|
||||
<button id="submit" type="submit">generate keys & register</button>
|
||||
</form>
|
||||
</div>
|
||||
<div id="threads" class="column hidden">
|
||||
<h1>vybe</h1>
|
||||
<h3>threads</h3>
|
||||
<div id="threadlist">loading...</div>
|
||||
<h3>create thread</h3>
|
||||
<form id="createthread">
|
||||
<label for="newthreadname">thread name</label>
|
||||
<input type="text" id="newthreadname" />
|
||||
<button id="submitthread" type="submit">create</button>
|
||||
</form>
|
||||
</div>
|
||||
<div id="chat" class="column hidden">
|
||||
<h3 class="thread">
|
||||
current thread: <strong id="threadname">meow</strong>
|
||||
</h3>
|
||||
<h3>messages will appear below as they are sent</h3>
|
||||
<button id="loadmore">load more messages</button>
|
||||
<div id="messages"></div>
|
||||
<form id="msginput">
|
||||
<input type="text" placeholder="write a message..." id="msg" />
|
||||
<button type="submit" class="hidden" id="sendmsg"></button>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
<head>
|
||||
<script src="/openpgp.min.js"></script>
|
||||
<script src="/socket.io.min.v4.6.1.js"></script>
|
||||
<script src="/chat.js"></script>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>vybe</title>
|
||||
<style>
|
||||
* {
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
|
||||
Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue",
|
||||
sans-serif;
|
||||
}
|
||||
body,
|
||||
button,
|
||||
input {
|
||||
background: #020202;
|
||||
color: #eaeaea;
|
||||
}
|
||||
body {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
margin: 0;
|
||||
min-width: min-content;
|
||||
padding: 0 20px;
|
||||
}
|
||||
.column {
|
||||
flex: 1;
|
||||
max-width: 50vw;
|
||||
overflow: hidden;
|
||||
}
|
||||
button {
|
||||
border-color: #767676;
|
||||
}
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
#msginput {
|
||||
margin-top: 15px;
|
||||
}
|
||||
.message {
|
||||
margin-bottom: 5px;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
#loadmore {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.channel {
|
||||
font-weight: normal;
|
||||
}
|
||||
.member {
|
||||
margin: 5px 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="register" class="hidden">
|
||||
<h1>welcome to vybe</h1>
|
||||
<h3>a communication network (beta)</h3>
|
||||
<p>
|
||||
to get started, you'll need an account. we use public key cryptography
|
||||
for security, rather than passwords. for now your keys are stored in
|
||||
your browser storage only.
|
||||
</p>
|
||||
<form id="registerform">
|
||||
<label for="name">name/username: </label>
|
||||
<input type="text" id="name" />
|
||||
<button id="submit" type="submit">generate keys & register</button>
|
||||
</form>
|
||||
</div>
|
||||
<div id="threads" class="column hidden">
|
||||
<h1>vybe</h1>
|
||||
<h3>threads</h3>
|
||||
<div id="threadlist">loading...</div>
|
||||
<h3>create thread</h3>
|
||||
<form id="createthread">
|
||||
<label for="newthreadname">thread name</label>
|
||||
<input type="text" id="newthreadname" /><br /><br />
|
||||
<span>thread permissions</span><br />
|
||||
<input type="radio" id="public" name="permissions" value="public" />
|
||||
<label for="public">anyone can view and post</label><br />
|
||||
<input
|
||||
type="radio"
|
||||
id="private_post"
|
||||
name="permissions"
|
||||
value="private_post"
|
||||
/>
|
||||
<label for="private_post">only members can post, anyone can view</label
|
||||
><br />
|
||||
<input
|
||||
type="radio"
|
||||
id="private_view"
|
||||
name="permissions"
|
||||
value="private_view"
|
||||
/>
|
||||
<label for="private_view">only members can view and post</label
|
||||
><br /><br />
|
||||
<span>members</span><br />
|
||||
<input type="text" id="membername" placeholder="username" /><button
|
||||
id="addmember"
|
||||
>
|
||||
add
|
||||
</button>
|
||||
<div id="memberlist"></div>
|
||||
<br />
|
||||
<button id="submitthread" type="submit">create</button>
|
||||
</form>
|
||||
</div>
|
||||
<div id="chat" class="column hidden">
|
||||
<h3 class="thread">
|
||||
current thread: <strong id="threadname">meow</strong>
|
||||
</h3>
|
||||
<h3>messages will appear below as they are sent</h3>
|
||||
<button id="loadmore" class="hidden">load more messages</button>
|
||||
<div id="messages"></div>
|
||||
<form id="msginput">
|
||||
<input type="text" placeholder="write a message..." id="msg" />
|
||||
<button type="submit" class="hidden" id="sendmsg"></button>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -28,14 +28,14 @@ CREATE TABLE permissions (
|
|||
value text,
|
||||
foreign key(user) references users(id),
|
||||
foreign key(thread) references threads(id)
|
||||
)
|
||||
);
|
||||
|
||||
CREATE TABLE members (
|
||||
thread integer,
|
||||
user integer,
|
||||
foreign key(user) references users(id),
|
||||
foreign key(thread) references threads(id)
|
||||
)
|
||||
);
|
||||
|
||||
CREATE TABLE posts (
|
||||
id integer primary key asc,
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
## permissions -> permission
|
||||
|
||||
- manage_permissions
|
||||
- add_users
|
||||
- remove_users
|
||||
- add_members
|
||||
- remove_members
|
||||
- view
|
||||
- post
|
||||
|
||||
|
|
9
index.js
9
index.js
|
@ -14,10 +14,17 @@ const PORT = process.env.PORT || 3435;
|
|||
|
||||
const actions = require("./src/actions");
|
||||
|
||||
io.cache = {};
|
||||
|
||||
io.on("connection", (socket) => {
|
||||
for (let action in actions) {
|
||||
socket.on(action, (msg) =>
|
||||
actions[action](msg, (response) => socket.emit(action, response), socket, io)
|
||||
actions[action](
|
||||
msg,
|
||||
(response) => socket.emit(action, response),
|
||||
socket,
|
||||
io
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const db = require("../db");
|
||||
const openpgp = require("openpgp");
|
||||
|
||||
const authenticate = async (msg, respond, socket) => {
|
||||
const authenticate = async (msg, respond, socket, io) => {
|
||||
if (!msg.name || !msg.message) {
|
||||
return respond({
|
||||
success: false,
|
||||
|
@ -43,6 +43,11 @@ const authenticate = async (msg, respond, socket) => {
|
|||
data[1],
|
||||
]);
|
||||
socket.userid = result.rows[0].id;
|
||||
if (io.cache[msg.name]) {
|
||||
io.cache[msg.name].push(socket.id);
|
||||
} else {
|
||||
io.cache[msg.name] = [socket.id];
|
||||
}
|
||||
return respond({
|
||||
success: true,
|
||||
});
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const db = require("../db");
|
||||
|
||||
const authwrap = (fn) => async (msg, respond, socket, io) => {
|
||||
if (!msg.__session) {
|
||||
if (!msg || !msg.__session) {
|
||||
return respond({
|
||||
success: false,
|
||||
message: "Not authenticated",
|
||||
|
|
|
@ -20,10 +20,82 @@ const create_thread = async (msg, respond, socket, io) => {
|
|||
"insert into threads (name, creator) values (?, ?) returning id",
|
||||
[msg.name, msg.auth_user.id]
|
||||
);
|
||||
io.emit('new_thread', {
|
||||
name: msg.name,
|
||||
id: insert.rows[0].id
|
||||
});
|
||||
const thread_id = insert.rows[0].id;
|
||||
// set up permissions
|
||||
if (!msg.permissions || !msg.permissions.view_limited) {
|
||||
await db.query(
|
||||
`insert into permissions (thread, type, flexible, permission, value)
|
||||
values (?, ?, ?, ?, ?)`,
|
||||
[thread_id, "everyone", false, "view", "true"]
|
||||
);
|
||||
if (!msg.permissions || !msg.permissions.post_limited) {
|
||||
await db.query(
|
||||
`insert into permissions (thread, type, flexible, permission, value)
|
||||
values (?, ?, ?, ?, ?)`,
|
||||
[thread_id, "everyone", false, "post", "true"]
|
||||
);
|
||||
} else {
|
||||
await db.query(
|
||||
`insert into permissions (thread, type, flexible, permission, value)
|
||||
values (?, ?, ?, ?, ?)`,
|
||||
[thread_id, "members", false, "post", "true"]
|
||||
);
|
||||
}
|
||||
} else {
|
||||
await db.query(
|
||||
`insert into permissions (thread, type, flexible, permission, value)
|
||||
values (?, ?, ?, ?, ?)`,
|
||||
[thread_id, "members", false, "view", "true"]
|
||||
);
|
||||
await db.query(
|
||||
`insert into permissions (thread, type, flexible, permission, value)
|
||||
values (?, ?, ?, ?, ?)`,
|
||||
[thread_id, "members", false, "post", "true"]
|
||||
);
|
||||
}
|
||||
// add members
|
||||
for (let user of msg.members) {
|
||||
// get user id
|
||||
const id = await db.query("select id from users where name = ?", [user]);
|
||||
if (id.rows.length > 0) {
|
||||
const user_id = id.rows[0].id;
|
||||
await db.query("insert into members (thread, user) values (?, ?)", [
|
||||
thread_id,
|
||||
user_id,
|
||||
]);
|
||||
}
|
||||
}
|
||||
const member_perms = {
|
||||
is_member: true,
|
||||
view: true,
|
||||
post: true,
|
||||
};
|
||||
const general_perms = {
|
||||
is_member: false,
|
||||
view: !msg.permissions || !msg.permissions.view_limited,
|
||||
post: !msg.permissions || !msg.permissions.post_limited,
|
||||
};
|
||||
for (let username in io.cache) {
|
||||
if (msg.members.includes(username)) {
|
||||
const sockets = io.cache[username];
|
||||
for (let s of sockets) {
|
||||
io.to(s).emit("new_thread", {
|
||||
name: msg.name,
|
||||
id: insert.rows[0].id,
|
||||
permissions: member_perms,
|
||||
});
|
||||
}
|
||||
} else if (general_perms.view) {
|
||||
const sockets = io.cache[username];
|
||||
for (let s of sockets) {
|
||||
io.to(s).emit("new_thread", {
|
||||
name: msg.name,
|
||||
id: insert.rows[0].id,
|
||||
permissions: general_perms,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
// respond
|
||||
return respond({
|
||||
success: true,
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
const db = require("../db");
|
||||
const authwrap = require("./authwrap");
|
||||
const check_permissions = require("./helpers/check_permissions");
|
||||
|
||||
const get_history = async (msg, respond) => {
|
||||
if (msg.before && isNaN(Number(msg.before))) {
|
||||
|
@ -13,6 +15,12 @@ const get_history = async (msg, respond) => {
|
|||
message: "thread ID required",
|
||||
});
|
||||
}
|
||||
if (!(await check_permissions(msg.auth_user.id, msg.thread)).view) {
|
||||
return respond({
|
||||
success: false,
|
||||
message: "you can't view this thread",
|
||||
});
|
||||
}
|
||||
const messages = await db.query(
|
||||
`select users.name, posts.id, content from posts
|
||||
join users on posts.user = users.id
|
||||
|
@ -31,4 +39,4 @@ const get_history = async (msg, respond) => {
|
|||
});
|
||||
};
|
||||
|
||||
module.exports = get_history;
|
||||
module.exports = authwrap(get_history);
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
const db = require("../../db");
|
||||
|
||||
const check_permissions = async (user_id, thread_id) => {
|
||||
// get all the permissions for the thread
|
||||
const permissions = await db.query(
|
||||
"select * from permissions where thread = ?",
|
||||
[thread_id]
|
||||
);
|
||||
// check if the user is a member
|
||||
const is_member =
|
||||
(
|
||||
await db.query("select * from members where thread = ? and user = ?", [
|
||||
thread_id,
|
||||
user_id,
|
||||
])
|
||||
).rows.length > 0;
|
||||
const get_permission = (permission) => {
|
||||
const relevant = permissions.rows.filter(
|
||||
(i) => i.permission === permission
|
||||
);
|
||||
for (let i of relevant) {
|
||||
if (i.type === "everyone" && i.value === "true") {
|
||||
return true;
|
||||
}
|
||||
if (i.type === "members" && i.value === "true" && is_member) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
return {
|
||||
is_member,
|
||||
view: get_permission("view"),
|
||||
post: get_permission("post"),
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = check_permissions;
|
|
@ -1,14 +1,32 @@
|
|||
const db = require("../db");
|
||||
const authwrap = require("./authwrap");
|
||||
const check_permissions = require("./helpers/check_permissions");
|
||||
|
||||
const list_threads = async (msg, respond) => {
|
||||
const threads = await db.query(
|
||||
"select name, id from threads order by created desc"
|
||||
`select name, id from threads
|
||||
join permissions on threads.id = permissions.thread
|
||||
join members on threads.id = members.thread
|
||||
where permissions.permission = 'view'
|
||||
and permissions.value = 'true'
|
||||
and ((permissions.type = 'everyone') or
|
||||
permissions.type = 'members' and members.user = ?)
|
||||
group by threads.id
|
||||
order by created desc`,
|
||||
[msg.auth_user.id]
|
||||
);
|
||||
// respond
|
||||
const rows = [];
|
||||
for (let i of threads.rows) {
|
||||
rows.push({
|
||||
...i,
|
||||
permissions: await check_permissions(msg.auth_user.id, i.id),
|
||||
});
|
||||
}
|
||||
return respond({
|
||||
success: true,
|
||||
threads: threads.rows,
|
||||
threads: rows,
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = list_threads;
|
||||
module.exports = authwrap(list_threads);
|
||||
|
|
|
@ -1,24 +1,60 @@
|
|||
const db = require("../db");
|
||||
const authwrap = require("./authwrap");
|
||||
const check_permissions = require("./helpers/check_permissions");
|
||||
|
||||
const send_message = async (msg, respond, socket) => {
|
||||
const send_message = async (msg, respond, socket, io) => {
|
||||
if (!msg.thread) {
|
||||
return respond({
|
||||
success: false,
|
||||
message: "thread ID required",
|
||||
});
|
||||
}
|
||||
if (!(await check_permissions(msg.auth_user.id, msg.thread)).post) {
|
||||
return respond({
|
||||
success: false,
|
||||
message: "you can't post to this thread",
|
||||
});
|
||||
}
|
||||
// add message and send it to everyone
|
||||
const id = await db.query(
|
||||
"insert into posts (user, thread, content) values (?, ?, ?) returning id",
|
||||
[msg.auth_user.id, msg.thread, msg.message]
|
||||
);
|
||||
socket.broadcast.emit("new_message", {
|
||||
id: id.rows[0].id,
|
||||
name: msg.auth_user.name,
|
||||
message: msg.message,
|
||||
thread: msg.thread,
|
||||
});
|
||||
// get thread members
|
||||
const members = (
|
||||
await db.query(
|
||||
"select name from users join members on members.user = users.id where members.thread = ?",
|
||||
[msg.thread]
|
||||
)
|
||||
).rows.map((i) => i.name);
|
||||
// get perms
|
||||
const permissions = await db.query(
|
||||
"select * from permissions where thread = ? and type = 'everyone' and value = 'true' and permission = 'view'",
|
||||
[msg.thread]
|
||||
);
|
||||
for (let username in io.cache) {
|
||||
if (members.includes(username)) {
|
||||
const sockets = io.cache[username];
|
||||
for (let s of sockets) {
|
||||
io.to(s).emit("new_message", {
|
||||
id: id.rows[0].id,
|
||||
name: msg.auth_user.name,
|
||||
message: msg.message,
|
||||
thread: msg.thread,
|
||||
});
|
||||
}
|
||||
} else if (permissions.rows.length > 0) {
|
||||
const sockets = io.cache[username];
|
||||
for (let s of sockets) {
|
||||
io.to(s).emit("new_message", {
|
||||
id: id.rows[0].id,
|
||||
name: msg.auth_user.name,
|
||||
message: msg.message,
|
||||
thread: msg.thread,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return respond({
|
||||
success: true,
|
||||
id: id.rows[0].id,
|
||||
|
|
Loading…
Reference in New Issue