displaynames, unselect thread, other fixes
							parent
							
								
									8ef0172d11
								
							
						
					
					
						commit
						7deeb82a08
					
				|  | @ -17,16 +17,20 @@ function setVisibility() { | |||
| 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'); | ||||
| 		document.getElementById(`thread${window.currentThread.id}`).classList.remove('active'); | ||||
| 		let editform = document.getElementById('editthread'); | ||||
| 		if (editform) { | ||||
| 			editform.remove(); | ||||
| 			edit.textContent = 'edit'; | ||||
| 		} | ||||
| 		if (window.currentThread.id === this.thread.id) { | ||||
| 			document.getElementById('thread').classList.add('hidden'); | ||||
| 			window.currentThread = null; | ||||
| 			return; | ||||
| 		} | ||||
| 	} | ||||
| 	else | ||||
| 		document.getElementById('thread').classList.remove('hidden'); | ||||
| 	if (this.thread.permissions.admin) | ||||
| 		edit.classList.remove('hidden'); | ||||
| 	else | ||||
|  | @ -43,7 +47,7 @@ function chooseThread() { | |||
| 				switchTab(document.getElementById(this.tab = 'messagetab')); | ||||
| 			else | ||||
| 				loadSpace(spans => { | ||||
| 					if (spans) | ||||
| 					if (spans.length) | ||||
| 						switchTab(document.getElementById(this.tab = 'spacetab')); | ||||
| 					else if (window.currentThread.streams.length) | ||||
| 						switchTab(document.getElementById(this.tab = 'streamtab')); | ||||
|  | @ -246,6 +250,21 @@ function clickedTab(event) { | |||
| 	document.getElementById(`thread${window.currentThread.id}`).tab = event.target.id; | ||||
| } | ||||
| 
 | ||||
| function changeName(e) { | ||||
| 	let displayname = document.getElementById('newname').value; | ||||
| 	window.emit('update_user', { | ||||
| 		displayname | ||||
| 	}, msg => { | ||||
| 		if (!msg.success) { | ||||
| 			console.log('update_user error: ', msg.message); | ||||
| 			document.getElementById('savename').classList.remove('hidden'); | ||||
| 			return; | ||||
| 		} | ||||
| 		window.displayname = displayname; | ||||
| 		document.getElementById('savename').classList.add('hidden'); | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| // main app html
 | ||||
| document.body.append(html.node` | ||||
| 	<div id='home' class='column'> | ||||
|  | @ -263,7 +282,17 @@ document.body.append(html.node` | |||
| 	<!-- create thread column goes here --> | ||||
| 	<hr class='separator' color='#505050'> | ||||
| 	<div id='profile' class='column hidden'> | ||||
| 		<p><strong>authentication requests</strong></p> | ||||
| 		<label class='heading'>display name</label> | ||||
| 		<input id='newname' onkeyup=${function(event) { | ||||
| 			if (window.displayname === this.value) | ||||
| 				document.getElementById('savename').classList.add('hidden'); | ||||
| 			else if (event.key === 'Enter') | ||||
| 				changeName(); | ||||
| 			else | ||||
| 				document.getElementById('savename').classList.remove('hidden'); | ||||
| 		}}> | ||||
| 		<button class='hidden' id='savename' onclick=${changeName}>save</button> | ||||
| 		<label class='heading'>authentication requests</label> | ||||
| 		<div id='authrequests'></div> | ||||
| 	</div> | ||||
| 	<hr class='separator' color='#505050'> | ||||
|  | @ -298,6 +327,8 @@ document.body.append(html.node` | |||
| 	</div> | ||||
| `);
 | ||||
| 
 | ||||
| document.getElementById('newname').value = window.displayname; | ||||
| 
 | ||||
| function makeThread(thread) { | ||||
| 	let node = html.node` | ||||
| 		<div class='thread' onclick=${chooseThread}>${ | ||||
|  | @ -313,7 +344,7 @@ window.socket.on('thread', thread => { | |||
| 	if (el) { | ||||
| 		el.thread = thread; | ||||
| 		el.textContent = thread.name; | ||||
| 		if (window.currentThread.id === thread.id) { | ||||
| 		if (window.currentThread?.id === thread.id) { | ||||
| 			Object.assign(window.currentThread, thread); | ||||
| 			document.getElementById('threadname').textContent = thread.name; | ||||
| 			setVisibility(); | ||||
|  | @ -323,21 +354,32 @@ window.socket.on('thread', thread => { | |||
| 	document.getElementById('threadlist').prepend(makeThread(thread)); | ||||
| }); | ||||
| 
 | ||||
| function authRequest(authrequest) { | ||||
| 	const p = html.node` | ||||
| 		<p> | ||||
| 			<button onclick=${() => { | ||||
| 				window.emit('authorize_key', { | ||||
| 					id: authrequest.id | ||||
| 				}, msg => { | ||||
| 					if (!msg.success) | ||||
| 						console.log('authorize_key failed: ', msg.message); | ||||
| 				}); | ||||
| 				p.remove(); | ||||
| 			}}>approve</button> | ||||
| 			session <strong>${authrequest.id}</strong> | ||||
| 		</p>`; | ||||
| 	document.getElementById('authrequests').append(p); | ||||
| 	setTimeout(() => p.remove(), Date.now() - authrequest.time + 60000 * 5); | ||||
| } | ||||
| 
 | ||||
| window.socket.on('authrequest', authRequest); | ||||
| 
 | ||||
| window.emit('list_threads', {}, msg => { | ||||
| 	const threadlist = document.getElementById('threadlist'); | ||||
| 	threadlist.replaceChildren(...msg.threads.map(makeThread)); | ||||
| 	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); | ||||
| }); | ||||
| export { | ||||
| 	authRequest | ||||
| }; | ||||
|  |  | |||
|  | @ -23,7 +23,7 @@ async function auth() { | |||
| 			message: sig, | ||||
| 			pubkey: window.keys.armored.publicKey | ||||
| 		}, | ||||
| 		msg => { | ||||
| 		async msg => { | ||||
| 			let register = document.getElementById('register'); | ||||
| 			if (!msg.success) { | ||||
| 				console.log('authenticate failed', msg); | ||||
|  | @ -34,7 +34,9 @@ async function auth() { | |||
| 			localStorage.setItem('keys', JSON.stringify(window.keys.armored)); | ||||
| 			localStorage.setItem('name', window.name); | ||||
| 			register.classList.add('hidden'); | ||||
| 			import('/app.js'); | ||||
| 			window.displayname = msg.displayname; | ||||
| 			const { authRequest } = await import('/app.js'); | ||||
| 			msg.authrequests.forEach(authRequest); | ||||
| 		} | ||||
| 	); | ||||
| } | ||||
|  |  | |||
|  | @ -99,7 +99,7 @@ | |||
| 			} | ||||
| 			.column { | ||||
| 				flex: 1; | ||||
| 				margin: 2px; | ||||
| 				margin: 3px; | ||||
| 			} | ||||
| 			.separator { | ||||
| 				margin: 8px 2px; | ||||
|  | @ -116,7 +116,6 @@ | |||
| 				justify-content: space-between; | ||||
| 			} | ||||
| 			#threads { | ||||
| 				margin: 3px; | ||||
| 				min-height: 0; | ||||
| 				display: flex; | ||||
| 				flex-direction: column; | ||||
|  | @ -130,15 +129,9 @@ | |||
| 			} | ||||
| 			#profile { | ||||
| 				max-width: 250px; | ||||
| 				> * { | ||||
| 					margin: 4px; | ||||
| 				} | ||||
| 			} | ||||
| 			.authrequest { | ||||
| 				margin-block: 3px; | ||||
| 			} | ||||
| 			.thread { | ||||
| 				padding: 2px 4px; | ||||
| 				padding: 2px 3px; | ||||
| 				white-space: pre; | ||||
| 			} | ||||
| 			#newthread { | ||||
|  |  | |||
|  | @ -16,7 +16,7 @@ function sendMessage(e) { | |||
| let earliestMessage; | ||||
| 
 | ||||
| window.socket.on('new_message', message => { | ||||
| 	if (message.thread !== window.currentThread.id) | ||||
| 	if (message.thread !== window.currentThread?.id) | ||||
| 		return; | ||||
| 	const messages = document.getElementById('messages'); | ||||
| 	let scroll = messages.scrollHeight - messages.scrollTop <= messages.clientHeight; | ||||
|  | @ -73,7 +73,7 @@ function loadMessages(firstRender, callback) { | |||
| 				for (let message of msg.messages) | ||||
| 					messages.prepend(html.node` | ||||
|                   <div class='message'> | ||||
|                      <strong>${message.name}: </strong> | ||||
|                      <strong>${message.displayname}: </strong> | ||||
|                      ${message.message} | ||||
|                   </div>`); | ||||
| 			} | ||||
|  |  | |||
|  | @ -132,7 +132,7 @@ function loadStreams() { | |||
| } | ||||
| 
 | ||||
| window.socket.on('stream', async msg => { | ||||
| 	if (msg.thread !== window.currentThread.id) | ||||
| 	if (msg.thread !== window.currentThread?.id) | ||||
| 		return; | ||||
| 	let p = document.getElementById('stream' + msg.id); | ||||
| 	if (p) { | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| create table user ( | ||||
| 	id integer primary key asc, | ||||
| 	name text, | ||||
| 	displayname text, | ||||
| 	created timestamp default current_timestamp | ||||
| ); | ||||
| 
 | ||||
|  | @ -60,11 +61,3 @@ create table span ( | |||
| 	scale decimal, | ||||
| 	foreign key(thread) references thread(id) | ||||
| ); | ||||
| 
 | ||||
| insert into thread (name) values ("meow"); | ||||
| insert into permission  | ||||
| 	(thread, type, permission, value) values | ||||
| 	(1, "everyone", "view", "true"); | ||||
| insert into permission  | ||||
| 	(thread, type, permission, value) values | ||||
| 	(1, "everyone", "post", "true"); | ||||
|  |  | |||
|  | @ -11,10 +11,7 @@ const authwrap = (fn) => async (msg, respond, socket) => { | |||
| 	} | ||||
| 	return await fn({ | ||||
| 		...msg, | ||||
| 		auth_user: { | ||||
| 			id: vybe.users[socket.username].id, | ||||
| 			name: socket.username | ||||
| 		} | ||||
| 		auth_user: vybe.users[socket.username] | ||||
| 	}, respond, socket); | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -36,7 +36,8 @@ async function send_message(msg, respond) { | |||
| 			for (let s of vybe.users[username].sockets) { | ||||
| 				s.emit('new_message', { | ||||
| 					id: id.rows[0].id, | ||||
| 					name: msg.auth_user.name, | ||||
| 					username: msg.auth_user.name, | ||||
| 					displayname: msg.auth_user.displayname, | ||||
| 					message: msg.message, | ||||
| 					thread: msg.thread | ||||
| 				}); | ||||
|  |  | |||
|  | @ -341,7 +341,9 @@ async function edit_thread(msg, respond) { | |||
| 					permissions: { | ||||
| 						is_member: username in members, | ||||
| 						view: true, | ||||
| 						post: !msg.permissions || !msg.permissions.post_limited, | ||||
| 						post: !msg.permissions || !msg.permissions.post_limited | ||||
| 							|| username in members, | ||||
| 						admin: username === msg.auth_user.name && perms.admin, | ||||
| 						...permissions | ||||
| 					} | ||||
| 				}); | ||||
|  |  | |||
|  | @ -47,7 +47,7 @@ async function create_user(msg, respond) { | |||
| 		[insert.rows[0].id, msg.pubkey] | ||||
| 	) | ||||
| 	// respond
 | ||||
| 	return respond({ | ||||
| 	respond({ | ||||
| 		success: true, | ||||
| 		id: insert.rows[0].id | ||||
| 	}); | ||||
|  | @ -60,9 +60,9 @@ async function authenticate(msg, respond, socket) { | |||
| 			message: 'invalid message' | ||||
| 		}); | ||||
| 	} | ||||
| 	let userid = await db.query( | ||||
| 		`select user.id from user where name = ?`, [msg.name]); | ||||
| 	if (userid.rows.length === 0) { | ||||
| 	let user = (await db.query( | ||||
| 		`select id, displayname from user where name = ?`, [msg.name])).rows[0]; | ||||
| 	if (!user) { | ||||
| 		return respond({ | ||||
| 			success: false, | ||||
| 			message: 'user not found' | ||||
|  | @ -91,13 +91,14 @@ async function authenticate(msg, respond, socket) { | |||
| 				message: 'bad auth message' | ||||
| 			}); | ||||
| 		} | ||||
| 		let user = vybe.users[msg.name]; | ||||
| 		if (!user) | ||||
| 			user = vybe.users[msg.name] = { | ||||
| 				id: userid.rows[0].id, | ||||
| 				sockets: [], | ||||
| 				authrequests: {} | ||||
| 			}; | ||||
| 		if (!user.displayname) | ||||
| 			user.displayname = msg.name; | ||||
| 		user = vybe.users[msg.name] || (vybe.users[msg.name] = { | ||||
| 			...user, | ||||
| 			name: msg.name, | ||||
| 			sockets: [], | ||||
| 			authrequests: {} | ||||
| 		}); | ||||
| 		if (result.rows.length === 0) { | ||||
| 			// request auth from logged in sessions
 | ||||
| 			let id = key.getFingerprint().slice(0, 8); | ||||
|  | @ -105,7 +106,7 @@ async function authenticate(msg, respond, socket) { | |||
| 			if (!await new Promise(resolve => { | ||||
| 				user.authrequests[id] = { time, callback: resolve }; | ||||
| 				for (let s of user.sockets) | ||||
| 					s.emit('authrequest', { id, time }, resolve); | ||||
| 					s.emit('authrequest', { id, time }); | ||||
| 				setTimeout(() => { | ||||
| 					delete user.authrequests[id]; | ||||
| 					resolve(false); | ||||
|  | @ -124,27 +125,45 @@ async function authenticate(msg, respond, socket) { | |||
| 		socket.username = msg.name; | ||||
| 		user.sockets.push(socket); | ||||
| 		respond({ | ||||
| 			success: true | ||||
| 			success: true, | ||||
| 			displayname: user.displayname, | ||||
| 			authrequests: Object.entries(user.authrequests).map(authrequest => ({ | ||||
| 				id: authrequest[0], | ||||
| 				time: authrequest[1].time | ||||
| 			})) | ||||
| 		}); | ||||
| 		// 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({ | ||||
| 		respond({ | ||||
| 			success: false, | ||||
| 			message: 'message signature verification failed' | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| async function authorize_key(msg, respond) { | ||||
| 	let authrequest = vybe.users[msg.auth_user.name].authrequests[msg.id]; | ||||
| 	if (!authrequest) { | ||||
| 		return respond({ | ||||
| 			success: false, | ||||
| 			message: 'auth request not found' | ||||
| 		}); | ||||
| 	} | ||||
| 	authrequest.callback(true); | ||||
| 	respond({ | ||||
| 		success: true | ||||
| 	}); | ||||
| } | ||||
| 
 | ||||
| async function update_user(msg, respond) { | ||||
| 	await db.query(`update user set displayname = ?`, msg.displayname); | ||||
| 	vybe.users[msg.auth_user.name].displayname = msg.displayname; | ||||
| 	respond({ | ||||
| 		success: true | ||||
| 	}); | ||||
| } | ||||
| 
 | ||||
| async function get_keys(msg, respond) { | ||||
| 	// validate inputs
 | ||||
| 	if (!msg.names) { | ||||
|  | @ -165,7 +184,7 @@ async function get_keys(msg, respond) { | |||
| 		msg.names | ||||
| 	); | ||||
| 	// respond
 | ||||
| 	return respond({ | ||||
| 	respond({ | ||||
| 		success: true, | ||||
| 		keys: keys.rows | ||||
| 	}); | ||||
|  | @ -174,5 +193,7 @@ async function get_keys(msg, respond) { | |||
| module.exports = { | ||||
| 	create_user, | ||||
| 	authenticate, | ||||
| 	authorize_key: authwrap(authorize_key), | ||||
| 	update_user: authwrap(update_user), | ||||
| 	get_keys: authwrap(get_keys) | ||||
| }; | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue