edit thread
							parent
							
								
									e9f3b3f15a
								
							
						
					
					
						commit
						6531e692f7
					
				
							
								
								
									
										256
									
								
								client/app.js
								
								
								
								
							
							
						
						
									
										256
									
								
								client/app.js
								
								
								
								
							|  | @ -2,13 +2,34 @@ import { render, html } from '/uhtml.js'; | |||
| import loadMessages from '/message.js'; | ||||
| import loadSpace from '/space.js'; | ||||
| 
 | ||||
| function setVisibility() { | ||||
| 	document.getElementById('visibility').innerText = `${ | ||||
| 		window.currentThread.permissions.everyone.view.value ? | ||||
| 			'this thread is visible to everyone' : | ||||
| 			'members can view this thread'} | ||||
| 		${window.currentThread.permissions.everyone.post.value ? | ||||
| 			'anyone can post' : | ||||
| 			window.currentThread.permissions.members.post.value ? | ||||
| 			'only members can post' : 'some members can post'}`;
 | ||||
| } | ||||
| 
 | ||||
| function chooseThread() { | ||||
| 	const edit = document.getElementById('edit'); | ||||
| 	if (window.currentThread) { | ||||
| 		if (window.currentThread.id === this.thread.id) | ||||
| 			return; | ||||
| 		document.getElementById(`thread${window.currentThread.id}`) | ||||
| 			.classList.remove('active'); | ||||
| 		let editform = document.getElementById('editthread'); | ||||
| 		if (editform) { | ||||
| 			editform.remove(); | ||||
| 			edit.textContent = 'edit'; | ||||
| 		} | ||||
| 	} | ||||
| 	if (this.thread.permissions.admin) | ||||
| 		edit.classList.remove('hidden'); | ||||
| 	else | ||||
| 		edit.classList.add('hidden'); | ||||
| 	document.getElementById('threadname').textContent = this.thread.name; | ||||
| 	this.classList.add('active'); | ||||
| 	window.currentThread = this.thread; | ||||
|  | @ -19,15 +40,8 @@ function chooseThread() { | |||
| 		document.getElementById('msginput').classList.add('hidden'); | ||||
| 	switchTab(document.getElementById(this.tab)); | ||||
| 	window.emit('get_thread', { thread: this.thread.id }, msg => { | ||||
| 		window.currentThread.members = msg.thread.members; | ||||
| 		document.getElementById('visibility').innerText = `${ | ||||
| 			msg.thread.permissions.everyone?.view.value === 'true' ? | ||||
| 				'this thread is visible to everyone' : | ||||
| 				'members can view this thread'} | ||||
| 			${msg.thread.permissions.everyone?.post.value === 'true' ? | ||||
| 				'anyone can post' : | ||||
| 				msg.thread.permissions.members?.post.view ? | ||||
| 				'only members can post' : 'select members can post'}`;
 | ||||
| 		window.currentThread = msg.thread; | ||||
| 		setVisibility(); | ||||
| 		document.getElementById('memberlist').replaceChildren( | ||||
| 			...msg.thread.members.map(member => | ||||
| 				html.node`<p class='member'>${member.name}</p>`) | ||||
|  | @ -48,48 +62,23 @@ function switchTab(tab) { | |||
| 		loadSpace(); | ||||
| } | ||||
| 
 | ||||
| function addMember() { | ||||
| 	const name = document.getElementById('membername').value; | ||||
| 	window.threadmembers.push(name); | ||||
| 	document | ||||
| 		.getElementById('newmembers') | ||||
| 		.appendChild(html.node`<p class='member'>${name}</p>`); | ||||
| 	document.getElementById('membername').value = ''; | ||||
| } | ||||
| 
 | ||||
| async function createThread(event) { | ||||
| 	event.preventDefault(); | ||||
| 	let name = document.getElementById('newthreadname').value; | ||||
| 	if (!name) { | ||||
| 		document.getElementById('nameempty').classList.remove('hidden'); | ||||
| 		document.getElementById('newnameempty').classList.remove('hidden'); | ||||
| 		return; | ||||
| 	} | ||||
| 	let members = window.threadmembers.map(name => ({ name })); | ||||
| 	const perms = document.querySelector( | ||||
| 		'input[name="permissions"]:checked' | ||||
| 		'input[name="newpermissions"]:checked' | ||||
| 	).value; | ||||
| 	if (perms === 'private_view') | ||||
| 		members = ( | ||||
| 			await new Promise(resolve => | ||||
| 				window.emit('get_keys', { names: window.threadmembers }, resolve) | ||||
| 			) | ||||
| 		).keys; | ||||
| 	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 | ||||
| 		}; | ||||
| 	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 }; | ||||
| 		// generate key
 | ||||
| 		/* wip | ||||
| 		var buf = new Uint8Array(32); | ||||
|  | @ -100,18 +89,13 @@ async function createThread(event) { | |||
| 			const member = newmembers[i]; | ||||
| 			const sig = await openpgp.encrypt({ | ||||
| 				message: await openpgp.createMessage({ text: key }), | ||||
| 				signingKeys: window.keys.priv, | ||||
| 				signingKeys: window.keys.priv | ||||
| 			}); | ||||
| 		} | ||||
| 		*/ | ||||
| 	} | ||||
| 	window.emit( | ||||
| 		'create_thread', | ||||
| 		{ | ||||
| 			name, | ||||
| 			permissions, | ||||
| 			members | ||||
| 		}, | ||||
| 	window.emit('create_thread', | ||||
| 		{ name, permissions, members: window.threadmembers }, | ||||
| 		msg => { | ||||
| 			chooseThread.call(document.getElementById('thread' + msg.id)); | ||||
| 			// since the form exists, this will perform cleanup
 | ||||
|  | @ -121,52 +105,125 @@ async function createThread(event) { | |||
| 	); | ||||
| } | ||||
| 
 | ||||
| function addMember() { | ||||
| 	const name = document.getElementById('membername'); | ||||
| 	if (!name.value) | ||||
| 		return; | ||||
| 	window.threadmembers.push({ name: name.value }); | ||||
| 	document.getElementById('newmembers') | ||||
| 		.appendChild(html.node`<p class='member'>${name.value}</p>`); | ||||
| 	name.value = ''; | ||||
| } | ||||
| 
 | ||||
| function newThread() { | ||||
| 	let form = document.getElementById('createthread'); | ||||
| 	if (form) { | ||||
| 		form.remove(); | ||||
| 		document.getElementById('createseparator').remove(); | ||||
| 		document.getElementById('newthread').textContent = 'create'; | ||||
| 	} else { | ||||
| 		window.threadmembers = [window.name]; | ||||
| 		document.getElementById('home') | ||||
| 			.insertAdjacentElement('afterend', html.node` | ||||
| 				<hr id='createseparator' class='separator' color='#505050'>`)
 | ||||
| 			.insertAdjacentElement('afterend', html.node` | ||||
| 				<form id='createthread' class='column' onsubmit=${createThread}> | ||||
| 					<h3>create thread</h3> | ||||
| 					<label for='newthreadname' class='heading'>thread name</label> | ||||
| 					<p id='nameempty' class='hidden'>name cannot be empty</p> | ||||
| 					<input type='text' id='newthreadname' /> | ||||
| 					<p id='permissions'>thread permissions</p> | ||||
| 					<input type='radio' id='public' name='permissions' value='public' checked /> | ||||
| 					<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'>anyone can view, only members can post</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 /> | ||||
| 					<label class='heading' for='membername'>members</label> | ||||
| 					<input type='text' id='membername' placeholder='username' onkeydown=${event => { | ||||
| 						if (event.key === 'Enter') { | ||||
| 							event.preventDefault(); | ||||
| 							addMember(); | ||||
| 						} | ||||
| 					}}/> | ||||
| 					<button id='addmember' onclick=${addMember}>add</button> | ||||
| 					<div id='newmembers'> | ||||
| 						<p class='member'>${window.name}</p> | ||||
| 					</div> | ||||
| 					<br /> | ||||
| 					<button id='submitthread' type='submit'>create</button> | ||||
| 				</form> | ||||
| 			`);
 | ||||
| 		document.getElementById('newthread').textContent = 'cancel'; | ||||
| 		return; | ||||
| 	} | ||||
| 	window.threadmembers = [{ | ||||
| 		name: window.name, | ||||
| 		permissions: { admin: 'true' } | ||||
| 	}]; | ||||
| 	document.querySelector('#home + .separator').insertAdjacentElement('afterend', html.node` | ||||
| 		<form id='createthread' class='column' onsubmit=${createThread}> | ||||
| 			<h3>create thread</h3> | ||||
| 			<label for='newthreadname' class='heading'>thread name</label> | ||||
| 			<p id='newnameempty' class='hidden'>name cannot be empty</p> | ||||
| 			<input type='text' id='newthreadname' /> | ||||
| 			<p id='permissions'>thread permissions</p> | ||||
| 			<input type='radio' name='newpermissions' | ||||
| 				id='public' value='public' checked /> | ||||
| 			<label for='public'>anyone can view and post</label><br> | ||||
| 			<input type='radio' name='newpermissions' | ||||
| 				id='private_post' value='private_post' /> | ||||
| 			<label for='private_post'>anyone can view, only members can post</label><br> | ||||
| 			<input type='radio' name='newpermissions' | ||||
| 				id='private_view' value='private_view' /> | ||||
| 			<label for='private_view'>only members can view and post</label><br> | ||||
| 			<br> | ||||
| 			<label class='heading' for='membername'>members</label> | ||||
| 			<input type='text' id='membername' placeholder='username' onkeydown=${event => { | ||||
| 				if (event.key === 'Enter') { | ||||
| 					event.preventDefault(); | ||||
| 					addMember(); | ||||
| 				} | ||||
| 			}} /> | ||||
| 			<button id='addmember' type='button' onclick=${addMember}>add</button> | ||||
| 			<div id='newmembers'> | ||||
| 				<p class='member'>${window.name}</p> | ||||
| 			</div> | ||||
| 			<br> | ||||
| 			<button id='submitthread' type='submit'>create</button> | ||||
| 		</form>`); | ||||
| 	document.getElementById('newthread').textContent = 'cancel'; | ||||
| } | ||||
| 
 | ||||
| async function saveThread(event) { | ||||
| 	event.preventDefault(); | ||||
| 	let name = document.getElementById('editthreadname').value; | ||||
| 	if (!name) { | ||||
| 		document.getElementById('nameempty').classList.remove('hidden'); | ||||
| 		return; | ||||
| 	} | ||||
| 	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 }; | ||||
| 		// todo: generate key and encrypt
 | ||||
| 	} | ||||
| 	window.emit('edit_thread', | ||||
| 		{ id: window.currentThread.id, name, permissions }, | ||||
| 		msg => { | ||||
| 			if (!msg.success) { | ||||
| 				console.log('edit_thread failed: ', msg.message); | ||||
| 				return; | ||||
| 			} | ||||
| 			editThread(); | ||||
| 		}); | ||||
| } | ||||
| 
 | ||||
| function editThread() { | ||||
| 	let form = document.getElementById('editthread'); | ||||
| 	if (form) { | ||||
| 		form.remove(); | ||||
| 		document.getElementById('edit').textContent = 'edit'; | ||||
| 		return; | ||||
| 	} | ||||
| 	form = html.node` | ||||
| 		<form id='editthread' class='column' onsubmit=${saveThread}> | ||||
| 			<label for='editthreadname' class='heading'>thread name</label> | ||||
| 			<input type='text' id='editthreadname' /> | ||||
| 			<p id='nameempty' class='hidden'>name cannot be empty</p> | ||||
| 			<p id='permissions'>thread permissions</p> | ||||
| 			<input type='radio' name='permissions' | ||||
| 				id='public' value='public' /> | ||||
| 			<label for='public'>anyone can view and post</label><br> | ||||
| 			<input type='radio' name='permissions' | ||||
| 				id='private_post' value='private_post' /> | ||||
| 			<label for='private_post'>anyone can view, only members can post</label><br> | ||||
| 			<input type='radio' name='permissions' | ||||
| 				id='private_view' value='private_view' /> | ||||
| 			<label for='private_view'>only members can view and post</label><br> | ||||
| 			<br> | ||||
| 			<button id='savethread'>save</button> | ||||
| 		</form>`; | ||||
| 	form['editthreadname'].value = window.currentThread.name; | ||||
| 	if (window.currentThread.permissions.everyone.post.value) | ||||
| 		form['public'].checked = true; | ||||
| 	else if (window.currentThread.permissions.everyone.view.value) | ||||
| 		form['private_post'].checked = true; | ||||
| 	else | ||||
| 		form['private_view'].checked = true; | ||||
| 	document.querySelector('#thread').append(form); | ||||
| 	document.getElementById('edit').textContent = 'cancel'; | ||||
| } | ||||
| 
 | ||||
| function clickedTab(event) { | ||||
|  | @ -188,6 +245,8 @@ document.body.append(html.node` | |||
| 		}>${window.name}</div> | ||||
| 	</div> | ||||
| 	<hr class='separator' color='#505050'> | ||||
| 	<!-- create thread column goes here --> | ||||
| 	<hr class='separator' color='#505050'> | ||||
| 	<div id='profile' class='column hidden'> | ||||
| 		<p><strong>authentication requests</strong></p> | ||||
| 		<div id='authrequests'></div> | ||||
|  | @ -196,7 +255,8 @@ document.body.append(html.node` | |||
| 	<div id='thread' class='column'> | ||||
| 		<div id='content'> | ||||
| 			<div id='title'> | ||||
| 				thread: <strong id='threadname'>meow</strong> | ||||
| 				<span>thread: <strong id='threadname'>meow</strong></span> | ||||
| 				<button id='edit' class='hidden' onclick=${editThread}>edit</button> | ||||
| 			</div> | ||||
| 			<div id='buttons'> | ||||
| 				<div id='tabs'> | ||||
|  | @ -217,6 +277,7 @@ document.body.append(html.node` | |||
| 			<div id='memberlist'> | ||||
| 			</div> | ||||
| 		</div> | ||||
| 		<hr class='separator' color='#505050'> | ||||
| 	</div> | ||||
| `);
 | ||||
| 
 | ||||
|  | @ -231,7 +292,18 @@ function makeThread(thread) { | |||
| 	return node; | ||||
| } | ||||
| 
 | ||||
| window.socket.on('new_thread', thread => { | ||||
| window.socket.on('thread', thread => { | ||||
| 	let el = document.getElementById('thread' + thread.id); | ||||
| 	if (el) { | ||||
| 		el.thread = thread; | ||||
| 		el.textContent = thread.name; | ||||
| 		if (window.currentThread.id === thread.id) { | ||||
| 			Object.assign(window.currentThread, thread); | ||||
| 			document.getElementById('threadname').textContent = thread.name; | ||||
| 			setVisibility(); | ||||
| 		} | ||||
| 		return; | ||||
| 	} | ||||
| 	document.getElementById('threadlist').prepend(makeThread(thread)); | ||||
| }); | ||||
| 
 | ||||
|  |  | |||
|  | @ -48,7 +48,7 @@ | |||
| 			button, | ||||
| 			input, | ||||
| 			.tab { | ||||
| 				padding: 5px 7px; | ||||
| 				padding: 4px 7px; | ||||
| 			} | ||||
| 			input { | ||||
| 				background: #1b1b1b; | ||||
|  | @ -81,9 +81,11 @@ | |||
| 				margin-bottom: 5px; | ||||
| 				display: block; | ||||
| 			} | ||||
| 			h3, | ||||
| 			h3 { | ||||
| 				margin: 0; | ||||
| 			} | ||||
| 			h4 { | ||||
| 				margin: 10px 0; | ||||
| 				margin: 6px 0; | ||||
| 			} | ||||
| 			.hidden { | ||||
| 				display: none !important; | ||||
|  | @ -96,7 +98,8 @@ | |||
| 				margin: 8px 2px; | ||||
| 			} | ||||
| 			.separator:has(+ .separator), | ||||
| 			.separator:has(+ *.hidden) { | ||||
| 			.separator:has(+ *.hidden), | ||||
| 			.separator:last-child { | ||||
| 				display: none; | ||||
| 			} | ||||
| 			#home { | ||||
|  | @ -107,6 +110,12 @@ | |||
| 			} | ||||
| 			#threads { | ||||
| 				margin: 3px; | ||||
| 				min-height: 0; | ||||
| 				display: flex; | ||||
| 				flex-direction: column; | ||||
| 			} | ||||
| 			#threadlist { | ||||
| 				overflow: auto; | ||||
| 			} | ||||
| 			#user { | ||||
| 				padding: 6px; | ||||
|  | @ -127,25 +136,26 @@ | |||
| 			} | ||||
| 			#newthread { | ||||
| 				margin-top: 5px; | ||||
| 				width: fit-content; | ||||
| 			} | ||||
| 			#createthread { | ||||
| 				max-width: fit-content; | ||||
| 				overflow: auto; | ||||
| 			} | ||||
| 			#permissions { | ||||
| 				margin-bottom: 5px; | ||||
| 			} | ||||
| 			#thread { | ||||
| 				display: flex; | ||||
| 				margin: 0; | ||||
| 			} | ||||
| 			#content { | ||||
| 				margin: 2px; | ||||
| 				flex: 1; | ||||
| 				display: flex; | ||||
| 				flex-direction: column; | ||||
| 			} | ||||
| 			#title { | ||||
| 				margin: 4px; | ||||
| 			} | ||||
| 			#buttons { | ||||
| 			#title, #buttons { | ||||
| 				margin: 4px 2px; | ||||
| 				display: flex; | ||||
| 				justify-content: space-between; | ||||
|  | @ -194,8 +204,12 @@ | |||
| 					margin: 4px; | ||||
| 				} | ||||
| 			} | ||||
| 			#editthread { | ||||
| 				max-width: fit-content; | ||||
| 			} | ||||
| 			#visibility { | ||||
| 				white-space: pre-line; | ||||
| 				margin-bottom: 12px; | ||||
| 			} | ||||
| 			.member { | ||||
| 				margin: 5px 0; | ||||
|  |  | |||
|  | @ -7,31 +7,20 @@ const check_permission = async (user_id, thread_id) => { | |||
| 		[thread_id] | ||||
| 	); | ||||
| 	// check if the user is a member
 | ||||
| 	const is_member = | ||||
| 		( | ||||
| 			await db.query('select * from member where thread = ? and user = ?', [ | ||||
| 				thread_id, | ||||
| 				user_id, | ||||
| 			]) | ||||
| 	const is_member = ( | ||||
| 			await db.query('select * from member 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') | ||||
| 	}; | ||||
| 	let perms = { is_member }; | ||||
| 	for (let p of permissions.rows) { | ||||
| 		if (p.type === 'everyone' && p.value === 'true') | ||||
| 			perms[p.permission] = true; | ||||
| 		else if (p.type === 'members' && is_member && p.value === 'true') | ||||
| 			perms[p.permission] = true; | ||||
| 		else if (p.type === 'user' && p.user === user_id && p.value === 'true') | ||||
| 			perms[p.permission] = true; | ||||
| 	} | ||||
| 	return perms; | ||||
| }; | ||||
| 
 | ||||
| module.exports = check_permission; | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| const db = require('../db'); | ||||
| const authwrap = require('../authwrap'); | ||||
| 
 | ||||
| const create_thread = async (msg, respond, socket) => { | ||||
| const create_thread = async (msg, respond) => { | ||||
| 	// validate inputs
 | ||||
| 	if (typeof msg.name !== 'string') { | ||||
| 		return respond({ | ||||
|  | @ -26,52 +26,61 @@ const create_thread = async (msg, respond, socket) => { | |||
| 		await db.query( | ||||
| 			`insert into permission (thread, type, mutable, permission, value) 
 | ||||
| 			values (?, ?, ?, ?, ?)`,
 | ||||
| 			[thread_id, 'everyone', false, 'view', 'true'] | ||||
| 			[thread_id, 'everyone', true, 'view', 'true'] | ||||
| 		); | ||||
| 		if (!msg.permissions || !msg.permissions.post_limited) { | ||||
| 			await db.query( | ||||
| 				`insert into permission (thread, type, mutable, permission, value) 
 | ||||
| 				values (?, ?, ?, ?, ?)`,
 | ||||
| 				[thread_id, 'everyone', false, 'post', 'true'] | ||||
| 				[thread_id, 'everyone', true, 'post', 'true'] | ||||
| 			); | ||||
| 		} else { | ||||
| 			await db.query( | ||||
| 				`insert into permission (thread, type, mutable, permission, value) 
 | ||||
| 				values (?, ?, ?, ?, ?)`,
 | ||||
| 				[thread_id, 'members', false, 'post', 'true'] | ||||
| 				[thread_id, 'members', true, 'post', 'true'] | ||||
| 			); | ||||
| 		} | ||||
| 	} else { | ||||
| 		await db.query( | ||||
| 			`insert into permission (thread, type, mutable, permission, value) 
 | ||||
| 			values (?, ?, ?, ?, ?)`,
 | ||||
| 			[thread_id, 'members', false, 'view', 'true'] | ||||
| 			[thread_id, 'members', true, 'view', 'true'] | ||||
| 		); | ||||
| 		await db.query( | ||||
| 			`insert into permission (thread, type, mutable, permission, value) 
 | ||||
| 			values (?, ?, ?, ?, ?)`,
 | ||||
| 			[thread_id, 'members', false, 'post', 'true'] | ||||
| 			[thread_id, 'members', true, 'post', 'true'] | ||||
| 		); | ||||
| 	} | ||||
| 	// add members
 | ||||
| 	for (let user of msg.members) { | ||||
| 		if (!user) continue; | ||||
| 		// get user id
 | ||||
| 		const id = await db.query('select id from user where name = ?', [ | ||||
| 			user.name, | ||||
| 		]); | ||||
| 		if (id.rows.length > 0) { | ||||
| 			const user_id = id.rows[0].id; | ||||
| 	if (Array.isArray(msg.members)) { | ||||
| 		for (let member of msg.members) { | ||||
| 			if (!member) continue; | ||||
| 			// get user id
 | ||||
| 			const id = await db.query('select id from user where name = ?', [ | ||||
| 				member.name, | ||||
| 			]); | ||||
| 			if (id.rows.length === 0) { | ||||
| 				console.log('user not found: ' + member.name); | ||||
| 				continue; | ||||
| 			} | ||||
| 			await db.query( | ||||
| 				'insert into member (thread, user) values (?, ?)', | ||||
| 				[thread_id, user_id] | ||||
| 				[thread_id, id.rows[0].id] | ||||
| 			); | ||||
| 			if (typeof member.permissions === 'object') | ||||
| 				for (let permission in member.permissions) | ||||
| 					await db.query(` | ||||
| 						insert into permission (thread, type, user, mutable, permission, value) | ||||
| 						values (?, ?, ?, ?, ?, ?)`,
 | ||||
| 						[thread_id, 'user', id.rows[0].id, true, permission, member.permissions[permission]]); | ||||
| 		} | ||||
| 	} | ||||
| 	if (!msg.permissions || !msg.permissions.view_limited) { | ||||
| 		for (let username in vybe.users) { | ||||
| 			for (let socket of vybe.users[username].sockets) { | ||||
| 				socket.emit('new_thread', { | ||||
| 				socket.emit('thread', { | ||||
| 					name: msg.name, | ||||
| 					id: insert.rows[0].id, | ||||
| 					permissions: { | ||||
|  | @ -83,10 +92,12 @@ const create_thread = async (msg, respond, socket) => { | |||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	else { | ||||
| 	else if (Array.isArray(msg.members)) { | ||||
| 		for (let member of msg.members) { | ||||
| 			if (!vybe.users[member.name]) | ||||
| 				continue; | ||||
| 			for (let socket of vybe.users[member.name].sockets) { | ||||
| 				socket.emit('new_thread', { | ||||
| 				socket.emit('thread', { | ||||
| 					name: msg.name, | ||||
| 					id: insert.rows[0].id, | ||||
| 					permissions: { | ||||
|  | @ -101,7 +112,7 @@ const create_thread = async (msg, respond, socket) => { | |||
| 	// respond
 | ||||
| 	return respond({ | ||||
| 		success: true, | ||||
| 		id: insert.rows[0].id | ||||
| 		id: thread_id | ||||
| 	}); | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -0,0 +1,145 @@ | |||
| const db = require('../db'); | ||||
| const authwrap = require('../authwrap'); | ||||
| const check_permission = require('../check_permission'); | ||||
| 
 | ||||
| const edit_thread = async (msg, respond) => { | ||||
| 	// validate inputs
 | ||||
| 	if (!msg.id || typeof msg.name !== 'string') { | ||||
| 		return respond({ | ||||
| 			success: false, | ||||
| 			message: 'invalid msg' | ||||
| 		}); | ||||
| 	} | ||||
| 	if (msg.name.length > 200) { | ||||
| 		return respond({ | ||||
| 			success: false, | ||||
| 			message: 'thread name 200 chars max' | ||||
| 		}); | ||||
| 	} | ||||
| 	const perms = await check_permission(msg.auth_user.id, msg.id); | ||||
| 	if (!perms.admin) { | ||||
| 		return respond({ | ||||
| 			success: false, | ||||
| 			message: "user doesn't have permission" | ||||
| 		}); | ||||
| 	} | ||||
| 	// update name
 | ||||
| 	await db.query( | ||||
| 		'update thread set name = ? where id = ?', | ||||
| 		[msg.name, msg.id] | ||||
| 	); | ||||
| 	// update permissions
 | ||||
| 	let permissions = {}; | ||||
| 	for (const p of (await db.query( | ||||
| 		`select type, permission, value, mutable
 | ||||
| 		from permission | ||||
| 		where type != 'user' and thread = ?`,
 | ||||
| 		[msg.id] | ||||
| 	)).rows) { | ||||
| 		(permissions[p.type] || (permissions[p.type] = {})) | ||||
| 			[p.permission] = { | ||||
| 				value: p.value, | ||||
| 				mutable: p.mutable | ||||
| 			}; | ||||
| 	} | ||||
| 	async function setPermission(type, permission, value) { | ||||
| 		if (permissions[type] && permissions[type][permission]) { | ||||
| 			if (!permissions[type][permission].mutable) { | ||||
| 				respond({ | ||||
| 					success: false, | ||||
| 					message: 'permission not mutable' | ||||
| 				}); | ||||
| 				return false; | ||||
| 			} | ||||
| 			if (permissions[type][permission].value !== value) { | ||||
| 				await db.query(` | ||||
| 					update permission set value = ? | ||||
| 					where thread = ? and type = ? and permission = ?`,
 | ||||
| 					[value, msg.id, type, permission] | ||||
| 				); | ||||
| 				permissions[type][permission].value = value; | ||||
| 			} | ||||
| 		} | ||||
| 		else { | ||||
| 			await db.query( | ||||
| 				`insert into permission (thread, type, mutable, permission, value) 
 | ||||
| 				values (?, ?, ?, ?, ?)`,
 | ||||
| 				[msg.id, type, true, permission, value] | ||||
| 			); | ||||
| 			(permissions[type] || (permissions[type] = {})) | ||||
| 				[permission] = { | ||||
| 					value, | ||||
| 					mutable: true | ||||
| 				}; | ||||
| 		} | ||||
| 		return true; | ||||
| 	} | ||||
| 	if (!msg.permissions || !msg.permissions.view_limited) { | ||||
| 		if (!await setPermission('everyone', 'view', 'true')) | ||||
| 			return; | ||||
| 		if (!msg.permissions || !msg.permissions.post_limited) { | ||||
| 			if (!await setPermission('everyone', 'post', 'true')) | ||||
| 				return; | ||||
| 		} | ||||
| 		else { | ||||
| 			if (!await setPermission('members', 'post', 'true')) | ||||
| 				return; | ||||
| 			if (!await setPermission('everyone', 'post', 'false')) | ||||
| 				return; | ||||
| 		} | ||||
| 	} else { | ||||
| 		if (!await setPermission('members', 'view', 'true')) | ||||
| 			return; | ||||
| 		if (!await setPermission('members', 'post', 'true')) | ||||
| 			return; | ||||
| 		if (!await setPermission('everyone', 'view', 'false')) | ||||
| 			return; | ||||
| 		if (!await setPermission('everyone', 'post', 'false')) | ||||
| 			return; | ||||
| 	} | ||||
| 	if (!msg.permissions || !msg.permissions.view_limited) { | ||||
| 		for (let username in vybe.users) { | ||||
| 			for (let socket of vybe.users[username].sockets) { | ||||
| 				socket.emit('thread', { | ||||
| 					name: msg.name, | ||||
| 					id: msg.id, | ||||
| 					permissions: { | ||||
| 						is_member: false, | ||||
| 						view: true, | ||||
| 						post: !msg.permissions || !msg.permissions.post_limited, | ||||
| 						...permissions | ||||
| 					} | ||||
| 				}); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	else { | ||||
| 		for (let member of (await db.query( | ||||
| 			`select user.name from thread
 | ||||
| 			join member on thread.id = member.thread | ||||
| 			join user on user.id = member.user | ||||
| 			where thread.id = ?`,
 | ||||
| 			[msg.id] | ||||
| 		)).rows) { | ||||
| 			if (!vybe.users[member.name]) | ||||
| 				continue; | ||||
| 			for (let socket of vybe.users[member.name].sockets) { | ||||
| 				socket.emit('thread', { | ||||
| 					name: msg.name, | ||||
| 					id: msg.id, | ||||
| 					permissions: { | ||||
| 						is_member: true, | ||||
| 						view: true, | ||||
| 						post: true, | ||||
| 						...permissions | ||||
| 					} | ||||
| 				}); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return respond({ | ||||
| 		success: true | ||||
| 	}); | ||||
| }; | ||||
| 
 | ||||
| module.exports = authwrap(edit_thread); | ||||
|  | @ -9,7 +9,8 @@ const get_thread = async (msg, respond) => { | |||
| 			message: 'thread ID required' | ||||
| 		}); | ||||
| 	} | ||||
| 	if (!(await check_permission(msg.auth_user.id, msg.thread)).view) { | ||||
| 	let perms = await check_permission(msg.auth_user.id, msg.thread); | ||||
| 	if (!perms.view) { | ||||
| 		return respond({ | ||||
| 			success: false, | ||||
| 			message: "you can't view this thread" | ||||
|  | @ -23,39 +24,50 @@ const get_thread = async (msg, respond) => { | |||
| 		[msg.thread] | ||||
| 	); | ||||
| 	const permissions = await db.query( | ||||
| 		`select permission.type, permission.user, permission.permission, permission.value, permission.mutable
 | ||||
| 		from thread | ||||
| 		join permission on thread.id = permission.thread | ||||
| 		where thread.id = ?`,
 | ||||
| 		`select type, user, permission, value, mutable
 | ||||
| 		from permission where thread = ?`,
 | ||||
| 		[msg.thread] | ||||
| 	); | ||||
| 	let threadperms = {}; | ||||
| 	let members = Object.fromEntries(thread.rows.map(member => [member.id, member.user])); | ||||
| 	let members = Object.fromEntries(thread.rows.map(member => | ||||
| 		[member.id, { name: member.user }] | ||||
| 	)); | ||||
| 	for (let permission of permissions.rows) { | ||||
| 		const member = members[permission.user]; | ||||
| 		if (member) { | ||||
| 			if (!member.permissions) | ||||
| 				member.permissions = {}; | ||||
| 			member.permissions[permission.permission] = { | ||||
| 				value: permission.value, | ||||
| 				mutable: permission.mutable | ||||
| 			}; | ||||
| 		} | ||||
| 		if (member) | ||||
| 			(member.permissions || (member.permissions = {})) | ||||
| 				[permission.permission] = { | ||||
| 					value: permission.value, | ||||
| 					mutable: permission.mutable | ||||
| 				}; | ||||
| 		else | ||||
| 			(threadperms[permission.type] || (threadperms[permission.type] = {})) | ||||
| 			(perms[permission.type] || (perms[permission.type] = {})) | ||||
| 				[permission.permission] = { | ||||
| 					value: permission.value, | ||||
| 					mutable: permission.mutable | ||||
| 				}; | ||||
| 	} | ||||
| 	function makeBool(type, permission) { | ||||
| 		if (perms[type]) { | ||||
| 			if (perms[type][permission]) | ||||
| 				perms[type][permission].value = perms[type][permission].value === 'true' ? true : false; | ||||
| 			else | ||||
| 				perms[type][permission] = { value: false, mutable: true }; | ||||
| 		} else | ||||
| 			(perms[type] = {})[permission] = { value: false, mutable: true }; | ||||
| 	} | ||||
| 	makeBool('everyone', 'view'); | ||||
| 	makeBool('everyone', 'post'); | ||||
| 	makeBool('members', 'view'); | ||||
| 	makeBool('members', 'post'); | ||||
| 	return respond({ | ||||
| 		success: true, | ||||
| 		thread: { | ||||
| 			id: msg.thread, | ||||
| 			name: thread.rows[0].name, | ||||
| 			permissions: threadperms, | ||||
| 			permissions: perms, | ||||
| 			members: Object.entries(members).map(member => ({ | ||||
| 				id: member[0], | ||||
| 				name: member[1] | ||||
| 				...member[1] | ||||
| 			})) | ||||
| 		} | ||||
| 	}); | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue