fixing js errors, adding password change form

This commit is contained in:
Jakub Doka 2024-11-18 10:31:30 +01:00
parent e89511b14c
commit f3879cb013
No known key found for this signature in database
GPG key ID: C6E9A89936B8C143
2 changed files with 102 additions and 30 deletions

View file

@ -44,8 +44,8 @@ async function getFmtInstance() {
return fmtInstance ??= (await fmtInstaceFuture).instance; return fmtInstance ??= (await fmtInstaceFuture).instance;
} }
/** @param {WebAssembly.Instance} instance @param {string} code @param {"fmt" | "minify"} action /** @param {WebAssembly.Instance} instance @param {string} code @param {"tok" | "fmt" | "minify"} action
* @returns {string | undefined} */ * @returns {string | Uint8Array | undefined} */
function modifyCode(instance, code, action) { function modifyCode(instance, code, action) {
let { let {
INPUT, INPUT_LEN, INPUT, INPUT_LEN,
@ -54,6 +54,7 @@ function modifyCode(instance, code, action) {
} = instance.exports; } = instance.exports;
let funs = { fmt, tok, minify }; let funs = { fmt, tok, minify };
let fun = funs[action];
if (!(true if (!(true
&& memory instanceof WebAssembly.Memory && memory instanceof WebAssembly.Memory
&& INPUT instanceof WebAssembly.Global && INPUT instanceof WebAssembly.Global
@ -61,9 +62,8 @@ function modifyCode(instance, code, action) {
&& OUTPUT instanceof WebAssembly.Global && OUTPUT instanceof WebAssembly.Global
&& OUTPUT_LEN instanceof WebAssembly.Global && OUTPUT_LEN instanceof WebAssembly.Global
&& funs.hasOwnProperty(action) && funs.hasOwnProperty(action)
&& typeof funs[action] === "function" && typeof fun === "function"
)) never(); )) never();
let fun = funs[action];
if (action !== "fmt") { if (action !== "fmt") {
INPUT = OUTPUT; INPUT = OUTPUT;
@ -133,7 +133,7 @@ function packPosts(posts, view) {
* @return {Uint8Array} */ * @return {Uint8Array} */
function bufSlice(mem, ptr, len) { function bufSlice(mem, ptr, len) {
return new Uint8Array(mem.buffer, ptr.value, return new Uint8Array(mem.buffer, ptr.value,
new DataView(mem.buffer).getUint32(len.value, true)); new DataView(mem.buffer).getUint32(len.value, true));
} }
/** @param {WebAssembly.Memory} mem /** @param {WebAssembly.Memory} mem
@ -283,27 +283,27 @@ async function bindCodeEdit(target) {
} }
/** /**
* @type {{ Array<string> }} * @type {Array<string>}
* to be synched with `enum TokenGroup` in bytecode/src/fmt.rs */ * to be synched with `enum TokenGroup` in bytecode/src/fmt.rs */
const TOK_CLASSES = [ const TOK_CLASSES = [
'Blank', 'Blank',
'Comment', 'Comment',
'Keyword', 'Keyword',
'Identifier', 'Identifier',
'Directive', 'Directive',
'Number', 'Number',
'String', 'String',
'Op', 'Op',
'Assign', 'Assign',
'Paren', 'Paren',
'Bracket', 'Bracket',
'Colon', 'Colon',
'Comma', 'Comma',
'Dot', 'Dot',
'Ctor', 'Ctor',
]; ];
/** @type {{ [key: string]: (el: HTMLElement) => undefined | Promise<undefined> }} */ /** @type {{ [key: string]: (el: HTMLElement) => void | Promise<void> }} */
const applyFns = { const applyFns = {
timestamp: (el) => { timestamp: (el) => {
const timestamp = el.innerText; const timestamp = el.innerText;
@ -314,15 +314,16 @@ const applyFns = {
}; };
/** /**
* @param {HTMLElement} target * @param {HTMLElement} target */
* @param {string} code */
async function fmt(target) { async function fmt(target) {
const code = target.innerText; const code = target.innerText;
const instance = await getFmtInstance(); const instance = await getFmtInstance();
const decoder = new TextDecoder('utf-8'); const decoder = new TextDecoder('utf-8');
const fmt = modifyCode(instance, code, 'fmt'); const fmt = modifyCode(instance, code, 'fmt');
const codeBytes = new TextEncoder('utf-8').encode(fmt); if (typeof fmt !== "string") never()
const codeBytes = new TextEncoder().encode(fmt);
const tok = modifyCode(instance, fmt, 'tok'); const tok = modifyCode(instance, fmt, 'tok');
if (!(tok instanceof Uint8Array)) never();
target.innerHTML = ''; target.innerHTML = '';
let start = 0; let start = 0;
let kind = tok[0]; let kind = tok[0];
@ -429,6 +430,7 @@ if (window.location.hostname === 'localhost') {
const code = "main:=fn():void{return}"; const code = "main:=fn():void{return}";
const inst = await getFmtInstance() const inst = await getFmtInstance()
const fmtd = modifyCode(inst, code, "fmt") ?? never(); const fmtd = modifyCode(inst, code, "fmt") ?? never();
if (typeof fmtd !== "string") never();
const prev = modifyCode(inst, fmtd, "minify") ?? never(); const prev = modifyCode(inst, fmtd, "minify") ?? never();
if (code != prev) console.error(code, prev); if (code != prev) console.error(code, prev);
} }

View file

@ -64,6 +64,7 @@ async fn amain() {
.route("/profile", get(Profile::page)) .route("/profile", get(Profile::page))
.route("/profile-view", get(Profile::get)) .route("/profile-view", get(Profile::get))
.route("/profile/:name", get(Profile::get_other_page)) .route("/profile/:name", get(Profile::get_other_page))
.route("/profile/password", post(PasswordChange::post))
.route("/profile-view/:name", get(Profile::get_other)) .route("/profile-view/:name", get(Profile::get_other))
.route("/post", get(Post::page)) .route("/post", get(Post::page))
.route("/post-view", get(Post::get)) .route("/post-view", get(Post::get))
@ -339,6 +340,70 @@ impl fmt::Display for Post {
} }
} }
#[derive(Deserialize, Default)]
struct PasswordChange {
old_password: String,
new_password: String,
#[serde(skip)]
error: Option<&'static str>,
}
impl PasswordChange {
async fn post(
session: Session,
axum::Form(mut change): axum::Form<PasswordChange>,
) -> Html<String> {
db::with(|que| {
match que.authenticate.query_row((&session.name,), |r| r.get::<_, String>(1)) {
Ok(hash) if verify_password(&hash, &change.old_password).is_err() => {
change.error = Some("invalid credentials");
}
Ok(_) => {
let new_hashed = hash_password(&change.new_password);
match que
.change_passowrd
.execute((new_hashed, &session.name))
.log("execute update")
{
None => change.error = Some("intenal server error"),
Some(0) => change.error = Some("password is incorrect"),
Some(_) => {}
}
}
Err(rusqlite::Error::QueryReturnedNoRows) => {
change.error = Some("invalid credentials");
}
Err(e) => {
log::error!("login queri failed: {e}");
change.error = Some("internal server error");
}
}
});
if change.error.is_some() {
change.render(&session)
} else {
PasswordChange::default().render(&session)
}
}
}
impl Page for PasswordChange {
fn render_to_buf(self, _: &Session, buf: &mut String) {
let Self { old_password, new_password, error } = self;
write_html! { (buf)
<form "hx-post"="/profile/password" "hx-swap"="outerHTML">
if let Some(e) = error { <div class="error">e</div> }
<input name="old_password" type="password" autocomplete="old-password"
placeholder="old password" value=old_password>
<input name="new_password" type="password" autocomplete="new-password" placeholder="new password"
value=new_password>
<input type="submit" value="submit">
</form>
}
}
}
#[derive(Default)] #[derive(Default)]
struct Profile { struct Profile {
other: Option<String>, other: Option<String>,
@ -357,14 +422,19 @@ impl Profile {
impl Page for Profile { impl Page for Profile {
fn render_to_buf(self, session: &Session, buf: &mut String) { fn render_to_buf(self, session: &Session, buf: &mut String) {
db::with(|db| { db::with(|db| {
let name = self.other.as_ref().unwrap_or(&session.name);
let iter = db let iter = db
.get_user_posts .get_user_posts
.query_map((self.other.as_ref().unwrap_or(&session.name),), Post::from_row) .query_map((name,), Post::from_row)
.log("get user posts query") .log("get user posts query")
.into_iter() .into_iter()
.flatten() .flatten()
.filter_map(|p| p.log("user post row")); .filter_map(|p| p.log("user post row"));
write_html! { (buf) write_html! { (*buf)
if name == &session.name {
|b|{PasswordChange::default().render_to_buf(session, b)}
}
for post in iter { for post in iter {
!{post} !{post}
} else { } else {
@ -399,7 +469,7 @@ struct Login {
impl PublicPage for Login { impl PublicPage for Login {
fn render_to_buf(self, buf: &mut String) { fn render_to_buf(self, buf: &mut String) {
let Login { name, password, error } = self; let Self { name, password, error } = self;
write_html! { (buf) write_html! { (buf)
<form "hx-post"="/login" "hx-swap"="outerHTML"> <form "hx-post"="/login" "hx-swap"="outerHTML">
if let Some(e) = error { <div class="error">e</div> } if let Some(e) = error { <div class="error">e</div> }
@ -439,13 +509,12 @@ impl Login {
data.error = Some("invalid credentials"); data.error = Some("invalid credentials");
} }
Err(e) => { Err(e) => {
log::error!("foo {e}"); log::error!("login queri failed: {e}");
data.error = Some("internal server error"); data.error = Some("internal server error");
} }
}); });
if data.error.is_some() { if data.error.is_some() {
log::error!("what {:?}", data);
Err(data.render()) Err(data.render())
} else { } else {
Ok(AppendHeaders([ Ok(AppendHeaders([
@ -682,6 +751,7 @@ mod db {
gen_queries! { gen_queries! {
pub struct Queries { pub struct Queries {
register: "INSERT INTO user (name, password_hash) VALUES(?, ?)", register: "INSERT INTO user (name, password_hash) VALUES(?, ?)",
change_passowrd: "UPDATE user SET password_hash = ? WHERE name = ?",
authenticate: "SELECT name, password_hash FROM user WHERE name = ?", authenticate: "SELECT name, password_hash FROM user WHERE name = ?",
login: "INSERT OR REPLACE INTO session (id, username, expiration) VALUES(?, ?, ?)", login: "INSERT OR REPLACE INTO session (id, username, expiration) VALUES(?, ?, ?)",
logout: "DELETE FROM session WHERE id = ?", logout: "DELETE FROM session WHERE id = ?",