some progress

This commit is contained in:
Jakub Doka 2024-10-09 00:17:13 +02:00
parent 13f63c7700
commit 1626734c1a
No known key found for this signature in database
GPG key ID: C6E9A89936B8C143
10 changed files with 879 additions and 604 deletions

View file

@ -4,8 +4,12 @@ version = "0.1.0"
edition = "2021"
[dependencies]
aes-gcm = { version = "0.10.3", default-features = false, features = ["aes", "rand_core"] }
ed25519-dalek = { version = "2.1.1", default-features = false, features = ["rand_core"] }
anyhow = "1.0.89"
axum = "0.7.7"
getrandom = "0.2.15"
rand_core = { version = "0.6.4", features = ["getrandom"] }
x25519-dalek = { version = "2.0.1", default-features = false }
htmlm = "0.3.0"
log = "0.4.22"
rusqlite = "0.32.1"
serde = { version = "1.0.210", features = ["derive"] }
time = "0.3.36"
tokio = { version = "1.40.0", features = ["rt"] }

88
depell/src/index.css Normal file
View file

@ -0,0 +1,88 @@
* {
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
}
body {
--primary: white;
--secondary: #EFEFEF;
--error: #ff3333;
--placeholder: #333333;
}
body {
--small-gap: 5px;
--monospace: 'Courier New', Courier, monospace;
nav {
display: flex;
justify-content: space-between;
align-items: center;
section:last-child {
display: flex;
gap: var(--small-gap);
}
}
main {
margin-top: var(--small-gap);
}
}
div.preview {
div.stats {
display: flex;
gap: var(--small-gap);
}
}
form {
display: flex;
flex-direction: column;
gap: var(--small-gap);
::placeholder {
color: var(--placeholder);
}
.error {
color: var(--error);
text-align: center;
}
}
pre,
textarea {
outline: none;
border: none;
background: var(--secondary);
padding: var(--small-gap);
padding-top: calc(var(--small-gap) * 1.5);
margin: var(--small-gap) 0px;
font-family: var(--monospace);
resize: none;
}
input {
font-size: inherit;
outline: none;
border: none;
background: var(--secondary);
padding: var(--small-gap);
}
input:is(:hover, :focus) {
background: white;
}
button {
border: none;
outline: none;
font-size: inherit;
background: var(--secondary);
}
button:hover:not(:active) {
background: white;
}

95
depell/src/index.js Normal file
View file

@ -0,0 +1,95 @@
//// @ts-check
if (window.location.hostname === 'localhost') {
let id; setInterval(async () => {
let new_id = await fetch('/hot-reload').then(reps => reps.text());
id ??= new_id;
if (id !== new_id) window.location.reload();
}, 300);
}
document.body.addEventListener('htmx:afterSwap', (ev) => {
wireUp(ev.target);
});
wireUp(document.body);
/** @param {HTMLElement} target */
function wireUp(target) {
execApply(target);
cacheInputs(target);
bindTextareaAutoResize(target);
}
/** @param {string} content @return {string} */
function fmtTimestamp(content) {
new Date(parseInt(content) * 1000).toLocaleString()
}
/** @param {HTMLElement} target */
function execApply(target) {
/**@type {HTMLElement}*/ let elem;
for (elem of target.querySelectorAll('[apply]')) {
const funcname = elem.getAttribute('apply');
elem.textContent = window[funcname](elem.textContent);
}
}
/** @param {HTMLElement} target */
function bindTextareaAutoResize(target) {
/**@type {HTMLTextAreaElement}*/ let textarea;
for (textarea of target.querySelectorAll("textarea")) {
textarea.style.height = textarea.scrollHeight + "px";
textarea.style.overflowY = "hidden";
textarea.addEventListener("input", function() {
textarea.style.height = "auto";
textarea.style.height = textarea.scrollHeight + "px";
});
textarea.onkeydown = (ev) => {
const selecting = textarea.selectionStart !== textarea.selectionEnd;
if (ev.key === "Tab") {
ev.preventDefault();
const prevPos = textarea.selectionStart;
textarea.value = textarea.value.slice(0, textarea.selectionStart) +
' ' + textarea.value.slice(textarea.selectionEnd);
textarea.selectionStart = textarea.selectionEnd = prevPos + 4;
}
if (ev.key === "Backspace" && textarea.selectionStart != 0 && !selecting) {
let i = textarea.selectionStart, looped = false;
while (textarea.value.charCodeAt(--i) === ' '.charCodeAt(0)) looped = true;
if (textarea.value.charCodeAt(i) === '\n'.charCodeAt(0) && looped) {
ev.preventDefault();
let toDelete = (textarea.selectionStart - (i + 1)) % 4;
if (toDelete === 0) toDelete = 4;
const prevPos = textarea.selectionStart;
textarea.value = textarea.value.slice(0, textarea.selectionStart - toDelete) +
textarea.value.slice(textarea.selectionEnd);
textarea.selectionStart = textarea.selectionEnd = prevPos - toDelete;
}
}
}
}
}
/** @param {HTMLElement} target */
function cacheInputs(target) {
/**@type {HTMLFormElement}*/ let form;
for (form of target.querySelectorAll('form')) {
const path = form.getAttribute('hx-post') || form.getAttribute('hx-delete');
if (!path) {
console.warn('form does not have a hx-post or hx-delete attribute', form);
continue;
}
/**@type {HTMLInputElement}*/ let input;
for (input of form.elements) {
if ('password submit button'.includes(input.type)) continue;
const key = path + input.name;
input.value = localStorage.getItem(key) ?? '';
input.addEventListener("input", (ev) => localStorage.setItem(key, ev.target.value));
}
}
}

File diff suppressed because it is too large Load diff

12
depell/src/post-page.html Normal file
View file

@ -0,0 +1,12 @@
<h3>About posting code</h3>
<p>
If you are unfammiliar with <a href="https://git.ablecorp.us/AbleOS/holey-bytes">hblang</a>, refer to the
<strong>hblang/README.md</strong> or
vizit <a href="/profile/mlokis">mlokis'es posts</a>. Preferably don't edit the code here.
</p>
<h3>Extra textarea features</h3>
<ul>
<li>proper tab behaviour</li>
<li>snap to previous tab boundary on "empty" lines</li>
</ul>

View file

51
depell/src/schema.sql Normal file
View file

@ -0,0 +1,51 @@
PRAGMA foreign_keys = ON;
CREATE TABLE IF NOT EXISTS user(
name TEXT NOT NULL,
password_hash TEXT NOT NULL,
PRIMARY KEY (name)
) WITHOUT ROWID;
CREATE TABLE IF NOT EXISTS session(
id BLOB NOT NULL,
username TEXT NOT NULL,
expiration INTEGER NOT NULL,
FOREIGN KEY (username) REFERENCES user (name)
PRIMARY KEY (username)
) WITHOUT ROWID;
CREATE UNIQUE INDEX IF NOT EXISTS
session_id ON session (id);
CREATE TABLE IF NOT EXISTS post(
name TEXT NOT NULL,
author TEXT,
timestamp INTEGER,
code TEXT NOT NULL,
FOREIGN KEY (author) REFERENCES user (name) ON DELETE SET NULL,
PRIMARY KEY (author, name)
);
CREATE TABLE IF NOT EXISTS import(
from_name TEXT NOT NULL,
from_author TEXT,
to_name TEXT NOT NULL,
to_author TEXT,
FOREIGN KEY (from_name, from_author) REFERENCES post (name, author),
FOREIGN KEY (to_name, to_author) REFERENCES post (name, author)
);
CREATE INDEX IF NOT EXISTS
dependencies ON import(from_name, from_author);
CREATE INDEX IF NOT EXISTS
dependants ON import(to_name, to_author);
CREATE TABLE IF NOT EXISTS run(
code_name TEXT NOT NULL,
code_author TEXT NOT NULL,
runner TEXT NOT NULL,
FOREIGN KEY (code_name, code_author) REFERENCES post (name, author),
FOREIGN KEY (runner) REFERENCES user(name),
PRIMARY KEY (code_name, code_author, runner)
);

View file

@ -0,0 +1,17 @@
<h1>Welcome to depell</h1>
<p>
Depell (dependency hell) is a simple "social" media site best compared to twitter, except that all you can post is
<a href="https://git.ablecorp.us/AbleOS/holey-bytes">hblang</a> code with no comments allowed. Instead of likes you
run the program, and instead of retweets you import the program as dependency. Run counts even when ran indirectly.
</p>
<p>
The backend only serves the code and frontend compiles and runs it locally. All posts are immutable.
</p>
<h2>Security?</h2>
<p>
All code runs in WASM (inside a holey-bytes VM until hblang compiles to wasm) and is controlled by JavaScript. WASM
cant do any form of IO without going trough JavaScript so as long as JS import does not allow wasm to execute
arbitrary JS code, WASM can act as a container inside the JS.
</p>

View file

@ -0,0 +1,11 @@
[package]
name = "wasm-hbfmt"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
[dependencies]
hblang = { version = "0.1.0", path = "../../hblang", default-features = false }
log = { version = "0.4.22", features = ["max_level_off"] }

View file

@ -0,0 +1,101 @@
#![no_std]
#![feature(slice_take)]
#![feature(str_from_raw_parts)]
use hblang::parser::ParserCtx;
#[cfg(target_arch = "wasm32")]
#[panic_handler]
fn handle_panic(_: &core::panic::PanicInfo) -> ! {
core::arch::wasm32::unreachable()
}
use core::{
alloc::{GlobalAlloc, Layout},
cell::UnsafeCell,
};
const ARENA_SIZE: usize = 128 * 1024;
#[repr(C, align(32))]
struct SimpleAllocator {
arena: UnsafeCell<[u8; ARENA_SIZE]>,
head: UnsafeCell<*mut u8>,
}
impl SimpleAllocator {
const fn new() -> Self {
SimpleAllocator {
arena: UnsafeCell::new([0; ARENA_SIZE]),
head: UnsafeCell::new(core::ptr::null_mut()),
}
}
unsafe fn reset(&self) {
(*self.head.get()) = self.arena.get().cast::<u8>().add(ARENA_SIZE);
}
}
unsafe impl Sync for SimpleAllocator {}
unsafe impl GlobalAlloc for SimpleAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let size = layout.size();
let align = layout.align();
let until = self.arena.get() as *mut u8;
let new_head = (*self.head.get()).sub(size);
let aligned_head = (new_head as usize & (1 << (align - 1))) as *mut u8;
if until > aligned_head {
return core::ptr::null_mut();
}
*self.head.get() = aligned_head;
aligned_head
}
unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {
/* lol */
}
}
#[cfg_attr(target_arch = "wasm32", global_allocator)]
static ALLOCATOR: SimpleAllocator = SimpleAllocator::new();
const MAX_OUTPUT_SIZE: usize = 1024 * 10;
#[no_mangle]
static mut OUTPUT: [u8; MAX_OUTPUT_SIZE] = unsafe { core::mem::zeroed() };
#[no_mangle]
static mut OUTPUT_LEN: usize = 0;
#[no_mangle]
unsafe extern "C" fn fmt(code: *const u8, len: usize) {
ALLOCATOR.reset();
let code = core::str::from_raw_parts(code, len);
let arena = hblang::parser::Arena::default();
let mut ctx = ParserCtx::default();
let exprs = hblang::parser::Parser::parse(&mut ctx, code, "source.hb", &|_, _| Ok(0), &arena);
struct Write<'a>(&'a mut [u8]);
impl core::fmt::Write for Write<'_> {
fn write_str(&mut self, s: &str) -> core::fmt::Result {
if let Some(m) = self.0.take_mut(..s.len()) {
m.copy_from_slice(s.as_bytes());
Ok(())
} else {
Err(core::fmt::Error)
}
}
}
let mut f = Write(unsafe { &mut OUTPUT[..] });
hblang::fmt::fmt_file(exprs, code, &mut f).unwrap();
unsafe { OUTPUT_LEN = MAX_OUTPUT_SIZE - f.0.len() };
}