diff --git a/depell/src/index.js b/depell/src/index.js index 040c30d..8b4fee3 100644 --- a/depell/src/index.js +++ b/depell/src/index.js @@ -44,8 +44,8 @@ async function getFmtInstance() { return fmtInstance ??= (await fmtInstaceFuture).instance; } -/** @param {WebAssembly.Instance} instance @param {string} code @param {"fmt" | "minify"} action - * @returns {string | undefined} */ +/** @param {WebAssembly.Instance} instance @param {string} code @param {"tok" | "fmt" | "minify"} action + * @returns {string | Uint8Array | undefined} */ function modifyCode(instance, code, action) { let { INPUT, INPUT_LEN, @@ -54,6 +54,7 @@ function modifyCode(instance, code, action) { } = instance.exports; let funs = { fmt, tok, minify }; + let fun = funs[action]; if (!(true && memory instanceof WebAssembly.Memory && INPUT instanceof WebAssembly.Global @@ -61,9 +62,8 @@ function modifyCode(instance, code, action) { && OUTPUT instanceof WebAssembly.Global && OUTPUT_LEN instanceof WebAssembly.Global && funs.hasOwnProperty(action) - && typeof funs[action] === "function" + && typeof fun === "function" )) never(); - let fun = funs[action]; if (action !== "fmt") { INPUT = OUTPUT; @@ -133,7 +133,7 @@ function packPosts(posts, view) { * @return {Uint8Array} */ function bufSlice(mem, ptr, len) { 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 @@ -283,27 +283,27 @@ async function bindCodeEdit(target) { } /** - * @type {{ Array }} + * @type {Array} * to be synched with `enum TokenGroup` in bytecode/src/fmt.rs */ const TOK_CLASSES = [ - 'Blank', - 'Comment', - 'Keyword', - 'Identifier', - 'Directive', - 'Number', - 'String', - 'Op', - 'Assign', - 'Paren', - 'Bracket', - 'Colon', - 'Comma', - 'Dot', - 'Ctor', + 'Blank', + 'Comment', + 'Keyword', + 'Identifier', + 'Directive', + 'Number', + 'String', + 'Op', + 'Assign', + 'Paren', + 'Bracket', + 'Colon', + 'Comma', + 'Dot', + 'Ctor', ]; -/** @type {{ [key: string]: (el: HTMLElement) => undefined | Promise }} */ +/** @type {{ [key: string]: (el: HTMLElement) => void | Promise }} */ const applyFns = { timestamp: (el) => { const timestamp = el.innerText; @@ -314,15 +314,16 @@ const applyFns = { }; /** - * @param {HTMLElement} target - * @param {string} code */ + * @param {HTMLElement} target */ async function fmt(target) { const code = target.innerText; const instance = await getFmtInstance(); const decoder = new TextDecoder('utf-8'); 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'); + if (!(tok instanceof Uint8Array)) never(); target.innerHTML = ''; let start = 0; let kind = tok[0]; @@ -429,6 +430,7 @@ if (window.location.hostname === 'localhost') { const code = "main:=fn():void{return}"; const inst = await getFmtInstance() const fmtd = modifyCode(inst, code, "fmt") ?? never(); + if (typeof fmtd !== "string") never(); const prev = modifyCode(inst, fmtd, "minify") ?? never(); if (code != prev) console.error(code, prev); } diff --git a/depell/src/main.rs b/depell/src/main.rs index ce51368..0b234e0 100644 --- a/depell/src/main.rs +++ b/depell/src/main.rs @@ -64,6 +64,7 @@ async fn amain() { .route("/profile", get(Profile::page)) .route("/profile-view", get(Profile::get)) .route("/profile/:name", get(Profile::get_other_page)) + .route("/profile/password", post(PasswordChange::post)) .route("/profile-view/:name", get(Profile::get_other)) .route("/post", get(Post::page)) .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, + ) -> Html { + 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) +
+ if let Some(e) = error {
e
} + + + +
+ } + } +} + #[derive(Default)] struct Profile { other: Option, @@ -357,14 +422,19 @@ impl Profile { impl Page for Profile { fn render_to_buf(self, session: &Session, buf: &mut String) { db::with(|db| { + let name = self.other.as_ref().unwrap_or(&session.name); let iter = db .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") .into_iter() .flatten() .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 { !{post} } else { @@ -399,7 +469,7 @@ struct Login { impl PublicPage for Login { fn render_to_buf(self, buf: &mut String) { - let Login { name, password, error } = self; + let Self { name, password, error } = self; write_html! { (buf)
if let Some(e) = error {
e
} @@ -439,13 +509,12 @@ impl Login { data.error = Some("invalid credentials"); } Err(e) => { - log::error!("foo {e}"); + log::error!("login queri failed: {e}"); data.error = Some("internal server error"); } }); if data.error.is_some() { - log::error!("what {:?}", data); Err(data.render()) } else { Ok(AppendHeaders([ @@ -682,6 +751,7 @@ mod db { gen_queries! { pub struct Queries { 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 = ?", login: "INSERT OR REPLACE INTO session (id, username, expiration) VALUES(?, ?, ?)", logout: "DELETE FROM session WHERE id = ?",