diff --git a/backend/data/users.py b/backend/data/users.py index 00746f1..71494c7 100644 --- a/backend/data/users.py +++ b/backend/data/users.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from hashlib import scrypt +# from hashlib import scrypt import hashlib import random import string @@ -90,8 +90,10 @@ def register_user(username: str, password_plaintext: str) -> User: raise UserRegistrationError("user already exists") +# def scrypt(password_plaintext: bytes, password_salt: bytes) -> bytes: +# return hashlib.scrypt(password_plaintext, salt=password_salt, n=8, r=8, p=1) def scrypt(password_plaintext: bytes, password_salt: bytes) -> bytes: - return hashlib.scrypt(password_plaintext, salt=password_salt, n=8, r=8, p=1) + return hashlib.pbkdf2_hmac('sha256', password_plaintext, password_salt, 100000) SALT_CHARACTERS = string.ascii_uppercase + string.ascii_lowercase + string.digits diff --git a/front-end/components/bloom.mjs b/front-end/components/bloom.mjs index 0b4166c..d50c42e 100644 --- a/front-end/components/bloom.mjs +++ b/front-end/components/bloom.mjs @@ -11,77 +11,79 @@ */ const createBloom = (template, bloom) => { - if (!bloom) return; - const bloomFrag = document.getElementById(template).content.cloneNode(true); - const bloomParser = new DOMParser(); + if (!bloom) return; + const bloomFrag = document.getElementById(template).content.cloneNode(true); + const bloomParser = new DOMParser(); - const bloomArticle = bloomFrag.querySelector("[data-bloom]"); - const bloomUsername = bloomFrag.querySelector("[data-username]"); - const bloomTime = bloomFrag.querySelector("[data-time]"); - const bloomTimeLink = bloomFrag.querySelector("a:has(> [data-time])"); - const bloomContent = bloomFrag.querySelector("[data-content]"); + const bloomArticle = bloomFrag.querySelector("[data-bloom]"); + const bloomUsername = bloomFrag.querySelector("[data-username]"); + const bloomTime = bloomFrag.querySelector("[data-time]"); + const bloomTimeLink = bloomFrag.querySelector("a:has(> [data-time])"); + const bloomContent = bloomFrag.querySelector("[data-content]"); - bloomArticle.setAttribute("data-bloom-id", bloom.id); - bloomUsername.setAttribute("href", `/profile/${bloom.sender}`); - bloomUsername.textContent = bloom.sender; - bloomTime.textContent = _formatTimestamp(bloom.sent_timestamp); - bloomTimeLink.setAttribute("href", `/bloom/${bloom.id}`); - bloomContent.replaceChildren( - ...bloomParser.parseFromString(_formatHashtags(bloom.content), "text/html") - .body.childNodes - ); + bloomArticle.setAttribute("data-bloom-id", bloom.id); + bloomUsername.setAttribute("href", `/profile/${bloom.sender}`); + bloomUsername.textContent = bloom.sender; + bloomTime.textContent = _formatTimestamp(bloom.sent_timestamp); + bloomTimeLink.setAttribute("href", `/bloom/${bloom.id}`); + bloomContent.replaceChildren( + ...bloomParser.parseFromString(_formatHashtags(bloom.content), "text/html") + .body.childNodes + ); - return bloomFrag; + return bloomFrag; }; function _formatHashtags(text) { - if (!text) return text; - return text.replace( - /\B#[^#]+/g, - (match) => `${match}` - ); + if (!text) return text; + return text.replace( + /\B#[^#]+/g, // does not grab # + /\B#[a-zA-Z0-9_]+/g, + + (match) => `${match}` + ); } function _formatTimestamp(timestamp) { - if (!timestamp) return ""; + if (!timestamp) return ""; - try { - const date = new Date(timestamp); - const now = new Date(); - const diffSeconds = Math.floor((now - date) / 1000); + try { + const date = new Date(timestamp); + const now = new Date(); + const diffSeconds = Math.floor((now - date) / 1000); - // Less than a minute - if (diffSeconds < 60) { - return `${diffSeconds}s`; - } + // Less than a minute + if (diffSeconds < 60) { + return `${diffSeconds}s`; + } - // Less than an hour - const diffMinutes = Math.floor(diffSeconds / 60); - if (diffMinutes < 60) { - return `${diffMinutes}m`; - } + // Less than an hour + const diffMinutes = Math.floor(diffSeconds / 60); + if (diffMinutes < 60) { + return `${diffMinutes}m`; + } - // Less than a day - const diffHours = Math.floor(diffMinutes / 60); - if (diffHours < 24) { - return `${diffHours}h`; - } + // Less than a day + const diffHours = Math.floor(diffMinutes / 60); + if (diffHours < 24) { + return `${diffHours}h`; + } - // Less than a week - const diffDays = Math.floor(diffHours / 24); - if (diffDays < 7) { - return `${diffDays}d`; - } + // Less than a week + const diffDays = Math.floor(diffHours / 24); + if (diffDays < 7) { + return `${diffDays}d`; + } - // Format as month and day for older dates - return new Intl.DateTimeFormat("en-US", { - month: "short", - day: "numeric", - }).format(date); - } catch (error) { - console.error("Failed to format timestamp:", error); - return ""; - } + // Format as month and day for older dates + return new Intl.DateTimeFormat("en-US", { + month: "short", + day: "numeric", + }).format(date); + } catch (error) { + console.error("Failed to format timestamp:", error); + return ""; + } } -export {createBloom}; +export { createBloom }; diff --git a/front-end/views/profile.mjs b/front-end/views/profile.mjs index dd2b92a..9824d9c 100644 --- a/front-end/views/profile.mjs +++ b/front-end/views/profile.mjs @@ -1,66 +1,69 @@ -import {renderEach, renderOne, destroy} from "../lib/render.mjs"; +import { renderEach, renderOne, destroy } from "../lib/render.mjs"; import { - apiService, - state, - getLogoutContainer, - getLoginContainer, - getProfileContainer, - getTimelineContainer, + apiService, + state, + getLogoutContainer, + getLoginContainer, + getProfileContainer, + getTimelineContainer, } from "../index.mjs"; -import {createLogin, handleLogin} from "../components/login.mjs"; -import {createLogout, handleLogout} from "../components/logout.mjs"; -import {createProfile, handleFollow} from "../components/profile.mjs"; -import {createBloom} from "../components/bloom.mjs"; +import { createLogin, handleLogin } from "../components/login.mjs"; +import { createLogout, handleLogout } from "../components/logout.mjs"; +import { createProfile, handleFollow } from "../components/profile.mjs"; +import { createBloom } from "../components/bloom.mjs"; // Profile view - just this person's blooms and their profile function profileView(username) { - destroy(); + destroy(); - const existingProfile = state.profiles.find((p) => p.username === username); + const existingProfile = state.profiles.find((p) => p.username === username); - // Only fetch profile if we don't have it or if it's incomplete - if (!existingProfile || !existingProfile.recent_blooms) { - apiService.getProfile(username); - } + // Only fetch profile if we don't have it or if it's incomplete + if (!existingProfile || !existingProfile.recent_blooms) { + apiService.getProfile(username); + } - renderOne( - state.isLoggedIn, - getLogoutContainer(), - "logout-template", - createLogout - ); - document - .querySelector("[data-action='logout']") - ?.addEventListener("click", handleLogout); - renderOne( - state.isLoggedIn, - getLoginContainer(), - "login-template", - createLogin - ); - document - .querySelector("[data-action='login']") - ?.addEventListener("click", handleLogin); + renderOne( + state.isLoggedIn, + getLogoutContainer(), + "logout-template", + createLogout + ); + document + .querySelector("[data-action='logout']") + ?.addEventListener("click", handleLogout); + renderOne( + state.isLoggedIn, + getLoginContainer(), + "login-template", + createLogin + ); + document + // .querySelector("[data-action='login']") + // ?.addEventListener("click", handleLogin); + .querySelector(".login__form") + ?.addEventListener("submit", handleLogin); + //submit event gets data from inputs - const profileData = state.profiles.find((p) => p.username === username); - if (profileData) { - renderOne( - { - profileData, - whoToFollow: state.isLoggedIn ? state.whoToFollow : [], - isLoggedIn: state.isLoggedIn, - }, - getProfileContainer(), - "profile-template", - createProfile - ); - renderEach( - profileData.recent_blooms || [], - getTimelineContainer(), - "bloom-template", - createBloom - ); - } + const profileData = state.profiles.find((p) => p.username === username); + if (profileData) { + renderOne( + { + profileData, + whoToFollow: state.isLoggedIn ? state.whoToFollow : [], + isLoggedIn: state.isLoggedIn, + }, + getProfileContainer(), + "profile-template", + createProfile + ); + renderEach( + profileData.recent_blooms || [], + getTimelineContainer(), + "bloom-template", + createBloom + ); + } } -export {profileView}; +export { profileView };