merge celi changes
						commit
						133797889f
					
				|  | @ -36,7 +36,7 @@ after first run, the port is configured in instance.json. | |||
| > } | ||||
| > ``` | ||||
| 
 | ||||
| (the allow-origin header isn't necessarily required but works as a fallback in case websockets don't work for whatever reason) | ||||
| (the allow-origin header is necessary for federation to work) | ||||
| 
 | ||||
| then go to `https://vybe.example.domain` to start using vybe! | ||||
| 
 | ||||
|  | @ -45,6 +45,7 @@ then go to `https://vybe.example.domain` to start using vybe! | |||
| - encrypt private threads (users already use pgp) | ||||
| - video in calls | ||||
| - instance administration and moderation | ||||
| - notifications | ||||
| 
 | ||||
| let me know if you have any questions or issues. i can be reached via my website [jerl.zone](https://jerl.zone). | ||||
| if you want to contribute, contact me :) | ||||
|  |  | |||
|  | @ -71,7 +71,7 @@ async function auth() { | |||
| 
 | ||||
| async function submit(event) { | ||||
| 	event.preventDefault(); | ||||
| 	const name = document.getElementById('name').value; | ||||
| 	const name = document.getElementById('username').value; | ||||
| 	if (!name) | ||||
| 		return; | ||||
| 	const keys = await openpgp.generateKey({ | ||||
|  | @ -104,19 +104,18 @@ async function submit(event) { | |||
| render(document.body, html` | ||||
| 	<div id='register' class='hidden'> | ||||
| 		<h1>welcome to vybe</h1> | ||||
| 		<h3>a communication network (beta)</h3> | ||||
| 		<h3>a communication network</h3> | ||||
| 		<p>choose a username for your account.</p> | ||||
| 		<pre>vybe uses public key cryptography rather than passwords. | ||||
| your keys are stored in your browser's local storage only, so do this on a browser you can access again.</pre> | ||||
| 		<p> | ||||
| 			to get started, you'll need an account. we use public key cryptography | ||||
| 			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> | ||||
| 		<p> | ||||
| 			if you already have an account, enter your username here, | ||||
| 			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' /> | ||||
| 			<label for='username'>username: </label> | ||||
| 			<input id='username' type='text' /> | ||||
| 			<br> | ||||
| 			<button id='submit' type='submit'>register</button> | ||||
| 			<button onclick=${submit}>authenticate</button> | ||||
| 		</form> | ||||
|  |  | |||
|  | @ -127,16 +127,14 @@ | |||
| 				margin-block: 5px; | ||||
| 				word-wrap: break-word; | ||||
| 			} | ||||
| 			.thread:hover, | ||||
| 			.tab:hover, | ||||
| 			.instancetitle > span:hover, | ||||
| 			#user:hover { | ||||
| 				background-color: #333; | ||||
| 			} | ||||
| 			.thread.active, | ||||
| 			.tab.active, | ||||
| 			button:hover { | ||||
| 				background-color: #4f4f4f; | ||||
| 				background-color: #484848; | ||||
| 				color: #fff; | ||||
| 			} | ||||
| 			label.heading { | ||||
|  | @ -202,6 +200,9 @@ | |||
| 				align-items: center; | ||||
| 				min-height: 25px; | ||||
| 			} | ||||
| 			#username { | ||||
| 				margin-bottom: 10px; | ||||
| 			} | ||||
| 			#home { | ||||
| 				margin: 0; | ||||
| 				display: flex; | ||||
|  | @ -253,6 +254,13 @@ | |||
| 				overflow-y: auto; | ||||
| 			} | ||||
| 			.thread { | ||||
| 				&:hover { | ||||
| 					background-color: #303030; | ||||
| 				} | ||||
| 				&.active { | ||||
| 					background-color: #404040; | ||||
| 					color: #fff; | ||||
| 				} | ||||
| 				padding: 2px 3px; | ||||
| 				white-space: pre; | ||||
| 				cursor: default; | ||||
|  | @ -293,9 +301,9 @@ | |||
| 			} | ||||
| 			.tab { | ||||
| 				padding: 5px 7px; | ||||
| 				background-color: #1f1f1f; | ||||
| 				background-color: #222; | ||||
| 				border: 0; | ||||
| 				color: #ddd; | ||||
| 				color: #e0e0e0; | ||||
| 				font-weight: 500; | ||||
| 			} | ||||
| 			.tabcontent { | ||||
|  |  | |||
|  | @ -22,7 +22,7 @@ function loadMessages(callback) { | |||
| 				}); | ||||
| 				msg.value = ''; | ||||
| 			}}> | ||||
| 				<input id='msg' placeholder='write a message...' /> | ||||
| 				<input id='msg' placeholder='write a message...' autocomplete='off' /> | ||||
| 				<button type='submit' id='sendmsg'>send</button> | ||||
| 			</form> | ||||
| 		`);
 | ||||
|  |  | |||
|  | @ -17,11 +17,14 @@ function setVisibility() { | |||
| function openThread(div, pushState) { | ||||
| 	if (!document.getElementById('removemember').classList.contains('hidden')) | ||||
| 		document.getElementById('member').classList.add('hidden'); | ||||
| 	if (window.currentThread) | ||||
| 		window.currentThread.div.classList.remove('active'); | ||||
| 	if (!div) { | ||||
| 		document.getElementById('thread').classList.add('hidden'); | ||||
| 		window.currentThread = null; | ||||
| 		return; | ||||
| 	} | ||||
| 	div.classList.add('active'); | ||||
| 	document.getElementById('thread').classList.remove('hidden'); | ||||
| 	const edit = document.getElementById('edit'); | ||||
| 	if (div.thread.permissions.admin) { | ||||
|  | @ -32,9 +35,6 @@ function openThread(div, pushState) { | |||
| 		document.getElementById('membersadd').classList.add('hidden'); | ||||
| 	} | ||||
| 	document.getElementById('threadname').textContent = div.thread.name; | ||||
| 	if (window.currentThread) | ||||
| 		window.currentThread.div.classList.remove('active'); | ||||
| 	div.classList.add('active'); | ||||
| 	window.currentThread = div.thread; | ||||
| 	window.currentInstance = div.thread.instance; | ||||
| 	window.currentInstance.emit('get_thread', { thread: div.thread.id }, async msg => { | ||||
|  | @ -58,7 +58,9 @@ function openThread(div, pushState) { | |||
| 					} | ||||
| 					catch (e) { | ||||
| 						console.log(`error getting user ${member.id}: ${e}`); | ||||
| 						p.append(html.node`<span>${member.id}</span>`); | ||||
| 						let span = html.node`<span>${member.id}</span>`; | ||||
| 						span.user = member; | ||||
| 						p.append(span); | ||||
| 					} | ||||
| 				return p; | ||||
| 			})) | ||||
|  | @ -484,21 +486,21 @@ async function loadThreads(instancediv, select) { | |||
| 
 | ||||
| 		instancediv.instance.socket.on('thread', thread => { | ||||
| 			let el = threadlist.children['thread' + thread.id]; | ||||
| 			if (el) { | ||||
| 				Object.assign(el.thread, thread); | ||||
| 				el.children['name'].textContent = thread.name; | ||||
| 				if (!thread.permissions.everyone.view.value && !el.children['membericon']) | ||||
| 					el.insertAdjacentHTML('beforeend', `<img id='membericon' src='/members.png'>`); | ||||
| 				else if (el.children['membericon'] && thread.permissions.everyone.value) | ||||
| 					el.children['membericon'].remove(); | ||||
| 				if (window.currentThread?.id === thread.id) { | ||||
| 					Object.assign(window.currentThread, thread); | ||||
| 					document.getElementById('threadname').textContent = thread.name; | ||||
| 					setVisibility(); | ||||
| 				} | ||||
| 			if (!el) { | ||||
| 				threadlist.prepend(makeThread(thread)); | ||||
| 				return; | ||||
| 			} | ||||
| 			threadlist.prepend(makeThread(thread)); | ||||
| 			Object.assign(el.thread, thread); | ||||
| 			el.children['name'].textContent = thread.name; | ||||
| 			if (!thread.permissions.public && !el.children['membericon']) | ||||
| 				el.insertAdjacentHTML('beforeend', `<img id='membericon' src='/members.png'>`); | ||||
| 			else if (el.children['membericon'] && thread.permissions.public) | ||||
| 				el.children['membericon'].remove(); | ||||
| 			if (window.currentThread?.id === thread.id) { | ||||
| 				Object.assign(window.currentThread, thread); | ||||
| 				document.getElementById('threadname').textContent = thread.name; | ||||
| 				setVisibility(); | ||||
| 			} | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -82,11 +82,7 @@ global.vybe = { | |||
| const app = express(); | ||||
| app.use(compression()); | ||||
| const server = http.createServer(app); | ||||
| const io = new Server(server, { | ||||
| 	cors: { | ||||
| 		origin: true | ||||
| 	} | ||||
| }); | ||||
| const io = new Server(server); | ||||
| 
 | ||||
| app.use(express.static('client')); | ||||
| 
 | ||||
|  |  | |||
|  | @ -6,7 +6,7 @@ const path = 'vybe.db'; | |||
| const existed = fs.existsSync(path); | ||||
| const db = new sqlite3.Database(path); | ||||
| 
 | ||||
| db.query = function (sql, params) { | ||||
| db.query = function(sql, params) { | ||||
| 	return new Promise((resolve, reject) => { | ||||
| 		db.all(sql, params, (error, rows) => { | ||||
| 			if (error) | ||||
|  | @ -17,11 +17,12 @@ db.query = function (sql, params) { | |||
| 	}); | ||||
| }; | ||||
| 
 | ||||
| (async () => { | ||||
| db.ready = new Promise(async resolve => { | ||||
| 	if (!existed) | ||||
| 		for (let sql of fs.readFileSync('./db/1-init.sql').toString().split(';')) | ||||
| 			if (sql.trim()) | ||||
| 				await db.query(sql); | ||||
| })(); | ||||
| 	resolve(); | ||||
| }); | ||||
| 
 | ||||
| module.exports = db; | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ const db = require('../db'); | |||
| const authwrap = require('../authwrap'); | ||||
| const check_permission = require('../check_permission'); | ||||
| 
 | ||||
| (async () => { | ||||
| db.ready.then(async () => { | ||||
| 	for (let thread of (await db.query( | ||||
| 		`select name, id from thread` | ||||
| 	)).rows) { | ||||
|  | @ -36,7 +36,7 @@ const check_permission = require('../check_permission'); | |||
| 		}; | ||||
| 		vybe.calls[thread.id] = {}; | ||||
| 	} | ||||
| })(); | ||||
| }); | ||||
| 
 | ||||
| async function create_thread(msg, respond) { | ||||
| 	// validate inputs
 | ||||
|  | @ -129,6 +129,7 @@ async function create_thread(msg, respond) { | |||
| 				socket.emit('thread', { | ||||
| 					...thread, | ||||
| 					permissions: { | ||||
| 						public: true, | ||||
| 						view: true, | ||||
| 						post: !msg.permissions?.post_limited || members[id], | ||||
| 						admin: id === msg.auth_user.id | ||||
|  | @ -362,9 +363,7 @@ async function edit_thread(msg, respond) { | |||
| 	permissions.members.view.value = permissions.members.view.value === 'true'; | ||||
| 	permissions.members.post.value = permissions.members.post.value === 'true'; | ||||
| 	let members = Object.fromEntries((await db.query( | ||||
| 			`select member.user from thread
 | ||||
| 			join member on thread.id = member.thread | ||||
| 			where thread.id = ?`,
 | ||||
| 			`select user from member where thread = ?`, | ||||
| 			msg.id | ||||
| 		)).rows.map(row => [row.user, true])); | ||||
| 	if (!msg.permissions?.view_limited) { | ||||
|  | @ -373,6 +372,7 @@ async function edit_thread(msg, respond) { | |||
| 				socket.emit('thread', { | ||||
| 					...thread, | ||||
| 					permissions: { | ||||
| 						public: true, | ||||
| 						view: true, | ||||
| 						post: !msg.permissions?.post_limited || id in members, | ||||
| 						admin: id === msg.auth_user.id && perms.admin, | ||||
|  |  | |||
|  | @ -3,18 +3,21 @@ const authwrap = require('../authwrap'); | |||
| const openpgp = require('openpgp'); | ||||
| 
 | ||||
| async function create_user(msg, respond) { | ||||
| 	if (typeof msg.name !== 'string') { | ||||
| 	if (typeof msg.name !== 'string') | ||||
| 		return respond({ | ||||
| 			success: false, | ||||
| 			message: 'invalid username' | ||||
| 		}); | ||||
| 	} | ||||
| 	if (typeof msg.pubkey !== 'string') { | ||||
| 	if (typeof msg.pubkey !== 'string') | ||||
| 		return respond({ | ||||
| 			success: false, | ||||
| 			message: 'invalid public key' | ||||
| 		}); | ||||
| 	} | ||||
| 	if (msg.name.length > 50) | ||||
| 		return respond({ | ||||
| 			success: false, | ||||
| 			message: 'username cannot be longer than 50 characters' | ||||
| 		}); | ||||
| 	// ensure username is not taken
 | ||||
| 	const result = await db.query('select * from user where name = ?', [ | ||||
| 		msg.name | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue