login working

This commit is contained in:
metacryst
2025-12-24 03:21:22 -06:00
parent c92742e8a1
commit b4d0d77b91
16 changed files with 220 additions and 896 deletions

View File

@@ -36,26 +36,18 @@ export default class AuthHandler {
const payload = jwt.verify(token, process.env.JWT_SECRET);
const email = payload.email;
const user = db.members.getByEmail(email);
res.send({ email: user.email, name: user.firstName + " " + user.lastName });
res.send({ email: "sam@hyperia.so", name: "Sam Russell" });
} catch (err) {
res.status(401).send({ error: "Invalid token" });
}
}
async login(req, res) {
const { email, password } = req.body;
let foundUser = global.db.members.getByEmail(email)
if(!foundUser) {
res.status(400).json({ error: 'Incorrect email.' });
return;
}
const storedHash = foundUser.password
const valid = await argon2.verify(storedHash, password);
if (!valid) {
const { password } = req.body;
if (!(password === process.env.PASSWORD)) {
res.status(400).json({ error: 'Incorrect password.' });
} else {
const payload = { email: foundUser.email };
const payload = { email: "sam@hyperia.so" };
console.log(payload)
const secret = process.env.JWT_SECRET;
const options = { expiresIn: "2h" };
@@ -67,7 +59,7 @@ export default class AuthHandler {
sameSite: "lax", // like SameSiteLaxMode
maxAge: 2 * 60 * 60 * 1000, // 2 hours in milliseconds
path: "/", // available on entire site
domain: process.env.ENV === "production" ? "." + process.env.BASE_URL : undefined
domain: process.env.ENV === "production" ? process.env.BASE_URL : undefined
});
res.redirect("/")

File diff suppressed because one or more lines are too long

View File

@@ -43,10 +43,10 @@
}
body {
background-color: #2B311A;
color: var(--accent);
font-family: 'Bona Nova', sans-serif;
font-size: 16px;
background-color: var(--main);
color: var(--accent);
}
#title {

View File

@@ -1,133 +0,0 @@
css(`
app-menu {
color: var(--tan);
transform: translateX(-50%);
transition: transform .3s;
display: flex; gap: 2em; position: fixed; left: 50vw; bottom: 2em;
}
app-menu.minimized {
color: var(--accent);
transform: translate(-50%, 65%);
border: 0.2px solid var(--accent);
padding-top: 0.5em;
padding-left: 2em;
padding-right: 2em;
padding-bottom: 4em;
bottom: 1em;
border-radius: 12px;
}
app-menu p {
cursor: default;
transition: transform .3s, text-decoration .3s;
padding: 0.5em;
border-radius: 5px;
text-underline-offset: 5px;
}
app-menu p:hover {
text-decoration: underline;
transform: translateY(-5%)
}
app-menu p.touched {
text-decoration: underline;
transform: translateY(0%)
}
app-menu p.selected {
text-decoration: underline;
transform: translateY(-10%)
}
#divider.minimized {
display: none;
}
`)
register(
class AppMenu extends Shadow {
selected;
constructor(selected) {
super()
this.selected = selected
}
render() {
VStack(() => {
HStack(() => {
p("Forum")
p("Messages")
p("Market")
p("Jobs")
})
.justifyContent("center")
.gap(1.5, em)
.paddingRight(2, em)
img("/_/images/divider.svg", "40vw")
.attr({
"id": "divider",
})
})
.gap(0.5, em)
.onNavigate(() => {
if(window.location.pathname === "/") {
this.styleMaximized()
$("app-window").close()
} else {
this.styleMinimized()
$("app-window").open(this.selected)
}
})
.onAppear(() => {
Array.from(this.querySelectorAll("p")).forEach((el) => {
el.addEventListener("mousedown", (e) => {
el.classList.add("touched")
})
})
window.addEventListener("mouseup", (e) => {
let target = e.target
if(!target.matches("app-menu p")) {
return
}
target.classList.remove("touched")
if(target.classList.contains("selected")) {
this.selected = ""
window.navigateTo("/")
} else {
this.selected = target.innerText
window.navigateTo("/app/" + target.innerText.toLowerCase())
}
})
})
if(this.selected) {
this.styleMinimized()
}
}
styleMaximized() {
$$("app-menu p").forEach((el) => {
el.classList.remove("selected")
})
this.classList.remove("minimized")
$("#divider").style.display = ""
}
styleMinimized() {
$$("app-menu p").forEach((el) => {
if(el.innerText !== this.selected) {
el.classList.remove("selected")
} else {
el.classList.add("selected")
}
})
this.classList.add("minimized")
$("#divider").style.display = "none"
}
}
, "app-menu")

View File

@@ -1,54 +0,0 @@
import "../apps/Forum/Forum.js"
import "../apps/Tasks/Tasks.js"
import "../apps/Messages/Messages.js"
import "../apps/Market/Market.js"
import "../apps/Jobs/Jobs.js"
class AppWindow extends Shadow {
app;
constructor(app) {
super()
this.app = app
}
render() {
ZStack(() => {
switch(this.app) {
case "Forum":
Forum()
break;
case "Messages":
Messages()
break;
case "Market":
Market()
break;
case "Jobs":
Jobs()
break;
}
})
.position("fixed")
.display(this.app ? 'block' : 'none')
.width(100, "vw")
.height(100, "vh")
.background("#591d10")
.x(0)
.y(0)
// .backgroundImage("/_/images/fabric.png")
// .backgroundSize("33vw auto")
}
open(app) {
this.app = app
this.rerender()
}
close() {
this.style.display = "none"
}
}
register(AppWindow, "app-window")

View File

@@ -1,61 +1,20 @@
import "./AppWindow.js"
import "./AppMenu.js"
import "./ProfileButton.js"
import "./InputBox.js"
import "./Sidebar.js"
import "./LogTable.js"
class Home extends Shadow {
render() {
ZStack(() => {
img("/_/icons/logo.svg", "2.5em")
.position("fixed")
.left(3, em)
.top(3, vh)
.zIndex(3)
.onClick(() => {
window.navigateTo("/")
})
div()
.width(100, vw)
.height(100, vh)
.margin(0)
.backgroundImage("/_/images/the_return.webp")
.backgroundSize("cover")
.backgroundPosition("48% 65%")
.backgroundRepeat("no-repeat")
switch(window.location.pathname) {
case "/":
AppWindow()
AppMenu()
LogTable()
break
case "/app/jobs":
AppWindow("Jobs")
AppMenu("Jobs")
break;
case "/app/messages":
AppWindow("Messages")
AppMenu("Messages")
break;
case "/app/market":
AppWindow("Market")
AppMenu("Market")
break;
case "/app/forum":
AppWindow("Forum")
AppMenu("Forum")
break;
default:
throw new Error("Unknown route!")
}
HStack(() => {
ProfileButton()
.zIndex(1)
.cursor("default")
a("/signout", "Sign Out")
.background("transparent")

View File

@@ -0,0 +1,9 @@
class LogTable extends Shadow {
render() {
VStack(() => {
})
}
}
register(LogTable)

View File

@@ -6,7 +6,6 @@
<link rel="icon" href="/_/icons/logo.svg">
<link rel="stylesheet" href="/_/code/shared.css">
<script src="/_/code/quill.js"></script>
<script src="/_/code/zod.js"></script>
<script type="module" src="75820185/index.js"></script>
</head>
<body style="margin: 0px">

View File

@@ -6,12 +6,6 @@
<link rel="icon" href="/_/icons/logo.svg">
<link rel="stylesheet" href="/_/code/shared.css">
<style>
body {
font-size: 16px;
background-image: url("/_/images/fabric.webp");
background-size: 33vw auto; /* width height of each tile */
}
</style>
<script src="/_/code/quill.js"></script>
<script type="module" src="75820185/index.js"></script>

View File

@@ -1,325 +0,0 @@
class Events extends Shadow {
events = [
{
date: `January 23, 2025`,
title: `Hyperia Winter Ball`,
description: `Join us in Austin, Texas for a dance. Live music and drinks will be included. <br>Admission for men is $50, women are free. Open to the public.`,
location: `Austin, TX`
}
]
render() {
ZStack(() => {
VStack(() => {
h1("HYPERIA")
.marginBottom(0, em)
p("Public Events")
.fontSize(1.2, em)
.marginBottom(2, em)
const Stack = window.isMobile() ? VStack : HStack
Stack(() => {
VStack(() => {
p(`January 23, 2025`)
p(`Hyperia Winter Ball`)
.fontSize(1.2, em)
p(`Austin, TX`)
})
p(`Join us in Austin, Texas for a great dance, with free drinks and live music. <br><br>Admission: $35 for men, women are free.`)
.marginRight(4, em)
HStack(() => {
img("/_/icons/creditcards/visa.svg")
img("/_/icons/creditcards/mastercard.svg")
img("/_/icons/creditcards/discover.svg")
img("/_/icons/creditcards/amex.svg")
})
.alignSelf("flex-start")
.height(2, em)
.maxWidth(40, vw)
button("Buy Ticket")
.color("var(--darkbrown")
.border("1px solid #ab2f007d")
.background('var(--green)')
.marginLeft("auto")
.onClick(async function() {
this.innerText = "Loading..."
const res = await fetch("/create-checkout-session", { method: "POST" });
const data = await res.json();
window.location = data.url;
})
})
.gap(3, em)
.color("var(--darkbrown)")
.background(`var(--accent)`)
.padding(1, em)
.borderRadius(12, px)
.border("2px solid #ab2f007d")
})
.marginLeft(window.isMobile() ? 0 : 15, vmax)
.marginRight(window.isMobile() ? 0 : 15, vmax)
.marginTop(10, vmax)
HStack(() => {
p("Privacy Policy")
.onHover(function (hovering) {
if(hovering) {
this.style.color = "var(--darkbrown)"
} else {
this.style.color = ""
}
})
.onClick(() => {
this.$("#policyWindow").style.display = "flex"
})
p("Refund and Return Policy")
.onHover(function (hovering) {
if(hovering) {
this.style.color = "var(--darkbrown)"
} else {
this.style.color = ""
}
})
.onClick(() => {
this.$("#refundWindow").style.display = "flex"
})
p("Contact Us")
.onHover(function (hovering) {
if(hovering) {
this.style.color = "var(--darkbrown)"
} else {
this.style.color = ""
}
})
.onClick(() => {
this.$("#contactWindow").style.display = "flex"
})
})
.x(50, vw).yBottom(0, vh)
.center()
.gap(2, em)
.opacity(0.5)
.cursor("default")
})
VStack(() => {
p("Privacy Policy")
.fontSize(2, em)
.fontWeight(600)
.marginBottom(1, em)
p("We value your privacy. This Privacy Policy explains how we collect, use, store, and protect your information when you use our website or services.")
p("1. Information We Collect")
.fontWeight(600)
.marginTop(1, em)
p("• Personal information you provide, such as your name, email address, or other contact details.")
p("• Automatically collected data, including IP address, browser type, device information, and usage statistics.")
p("• Cookies or similar tracking technologies that help us improve the user experience.")
p("2. How We Use Your Information")
.fontWeight(600)
.marginTop(1, em)
p("• To operate and improve our website and services.")
p("• To communicate with you about updates, support requests, or account-related matters.")
p("• To maintain security, prevent fraud, and ensure proper functionality.")
p("3. How We Share Information")
.fontWeight(600)
.marginTop(1, em)
p("We do not sell your personal information. We may share data only with trusted service providers who help us operate the platform, or when required by law.")
p("4. Data Storage & Security")
.fontWeight(600)
.marginTop(1, em)
p("We use reasonable technical and administrative safeguards to protect your information. However, no system is completely secure, and we cannot guarantee absolute protection.")
p("5. Cookies")
.fontWeight(600)
.marginTop(1, em)
p("Our site may use cookies to remember preferences, analyze traffic, and enhance usability. You can disable cookies in your browser settings, but some features may stop working.")
p("6. Your Rights")
.fontWeight(600)
.marginTop(1, em)
p("Depending on your location, you may have rights to access, update, delete, or request a copy of your personal data. Contact us if you wish to exercise these rights.")
p("7. Third-Party Links")
.fontWeight(600)
.marginTop(1, em)
p("Our website may contain links to third-party sites. We are not responsible for their content or privacy practices.")
p("8. Changes to This Policy")
.fontWeight(600)
.marginTop(1, em)
p("We may update this Privacy Policy from time to time. Updated versions will be posted on this page with the effective date.")
p("9. Contact Us")
.fontWeight(600)
.marginTop(1, em)
p("If you have any questions about this Privacy Policy, feel free to contact us at info@hyperia.so.")
p("x")
.onClick(function (done) {
if(done) {
this.parentElement.style.display = "none"
}
})
.color("var(--red)")
.xRight(1, em).y(1, em)
.fontSize(2, em)
.cursor("pointer")
})
.x(50, vw).y(50, vh)
.width(70, vw).height(70, vh)
.center()
.backgroundColor("var(--accent)")
.display("none")
.overflow("scroll")
.padding(1, em)
.border("3px solid black")
.color("var(--darkbrown)")
.attr({ id: "policyWindow" })
VStack(() => {
p("Refund & Return Policy")
.fontSize(2, em)
.fontWeight(600)
.marginBottom(1, em)
p("1. Eligibility for Refunds")
.fontWeight(600)
.marginTop(1, em)
p("• Refund requests may be considered when submitted within 14 days of purchase.")
p("• To qualify, you must provide proof of purchase and a valid reason for the request.")
p("• Certain digital products or services may be non-refundable once accessed or downloaded.")
p("2. Non-Refundable Items")
.fontWeight(600)
.marginTop(1, em)
p("• Products or services that have already been delivered, downloaded, or accessed in full.")
p("• Custom work, personalized items, or one-time service fees.")
p("• Any promotional or discounted items, unless required by law.")
p("3. Returns (If Applicable)")
.fontWeight(600)
.marginTop(1, em)
p("• Physical items must be returned in their original condition.")
p("• You are responsible for return shipping costs unless the item was defective or incorrect.")
p("• Items damaged through misuse or neglect cannot be returned.")
p("4. Processing Refunds")
.fontWeight(600)
.marginTop(1, em)
p("• Approved refunds are issued to the original payment method.")
p("• Processing times may vary depending on your bank or payment provider.")
p("• We will notify you once your refund has been initiated.")
p("5. Cancellations")
.fontWeight(600)
.marginTop(1, em)
p("• Orders or subscriptions may be cancelled before fulfillment or renewal.")
p("• If Hyperia declare a cancellation of any product or event, a refund will be issued to all parties.")
p("6. Contact for Refund Requests")
.fontWeight(600)
.marginTop(1, em)
p("If you need to request a refund, return an item, or cancel an order, please contact us at info@hyperia.so. Include your order number and relevant details so we can assist you promptly.")
p("7. Policy Updates")
.fontWeight(600)
.marginTop(1, em)
p("We may update this Refund & Return Policy from time to time. Any changes will be posted on this page with the effective date.")
p("x")
.onClick(function (done) {
if(done) {
this.parentElement.style.display = "none"
}
})
.color("var(--red)")
.xRight(1, em).y(1, em)
.fontSize(2, em)
.cursor("pointer")
})
.x(50, vw).y(50, vh)
.width(70, vw).height(70, vh)
.center()
.backgroundColor("var(--accent)")
.display("none")
.overflow("scroll")
.padding(1, em)
.border("3px solid black")
.color("var(--darkbrown)")
.attr({ id: "refundWindow" })
VStack(() => {
p("Contact Us")
.fontSize(2, em)
.fontWeight(600)
.marginBottom(1, em)
p("Email: info@hyperia.so")
p("Phone: 813-373-9100")
p("Address: 2014 E 9th St, Unit A, Austin TX")
p("x")
.onClick(function (done) {
if(done) {
this.parentElement.style.display = "none"
}
})
.color("var(--red)")
.xRight(1, em).y(1, em)
.fontSize(2, em)
.cursor("pointer")
})
.gap(2, em)
.x(50, vw).y(50, vh)
.width(50, vw).height(50, vh)
.center()
.backgroundColor("var(--accent)")
.display("none")
.overflow("scroll")
.padding(1, em)
.border("3px solid black")
.color("var(--darkbrown)")
.attr({ id: "contactWindow" })
}
}
register(Events)

View File

@@ -1,79 +1,37 @@
import "../components/NavBar.js"
import "../components/SignupForm.js"
import "./Why.js"
import "./Events.js"
import "./Join.js"
import "./SignIn.js"
import "./Success.js"
class Home extends Shadow {
inputStyles(el) {
return el
.color("var(--accent)")
.border("1px solid var(--accent)")
}
render() {
ZStack(() => {
ZStack(() => {
if(window.location.search.includes("new")) {
p("Welcome to Hyperia! You may now log in.")
.x(50, vw).y(40, vh)
.center()
}
NavBar()
img("/_/icons/logo.svg", "2.5em")
.onClick((done) => {
if(!done) return
window.navigateTo("/")
form(() => {
input("Password")
.attr({name: "password", type: "password"})
.margin(1, em)
.styles(this.inputStyles)
button("Submit")
.margin(1, em)
})
.position("absolute")
.left(50, vw).top(4, em)
.attr({action: "/login", method: "POST"})
.x(50, vw).y(50, vh)
.center()
.transform(`translate(${window.isMobile() ? "-50%" : "-2em"}, -50%)`)
switch(window.location.pathname) {
case "/":
img("/_/images/knight.png", "29vmax")
.position("absolute")
.left(50, vw).top(isMobile() ? 50 : 53, vh)
.center()
p("H &nbsp; Y &nbsp; P &nbsp; E &nbsp; R &nbsp; I &nbsp; A &nbsp;")
.x(50, vw).y(isMobile() ? 50 : 53, vh)
.textAlign("center")
.center()
.color("var(--gold)")
.fontSize(isMobile() ? 6 : 5, vw)
.maxWidth(isMobile() ? 0.8 : 100, em)
if(!isMobile()) {
let text = "A Classical Christian Network"
p(isMobile() ? text : text.toUpperCase())
.x(50, vw).yBottom(isMobile() ? 1 : 3, vh)
.center()
.letterSpacing(0.3, em)
.width(isMobile() ? 80 : 100, vw)
.fontSize(isMobile() ? 0.8 : 1, em)
.textAlign("center")
}
break;
case "/why":
Why()
break;
case "/events":
Events()
break;
case "/join":
Join()
break;
case "/success":
Success()
break;
default:
if(window.location.pathname.startsWith("/signup")) {
SignupForm()
} else if(window.location.pathname.startsWith("/signin")) {
SignIn()
}
}
})
.onNavigate(() => {
this.rerender()
})
})
.width(98, vw).height(98, vh)
.background("#7B413A")
.x(1, vw).y(1, vh)
}
}

View File

@@ -1,29 +0,0 @@
class Join extends Shadow {
render() {
VStack(() => {
p("Membership is invitation-only. Wait to meet one of us, or come to one of our events!")
// p("Membership is invitation-only. But sign up for our newsletter to hear about more events!")
// HStack(() => {
// input("Email", "40vmin")
// .attr({name: "email", type: "email"})
// button("Sign Up")
// .width(15, vmin)
// })
// .gap(1, em)
// .marginTop(1, em)
})
.alignItems("center")
.maxWidth(90, vw)
.x(50, vw).y(50, vh)
.center()
}
}
register(Join)

View File

@@ -1,9 +0,0 @@
class Success extends Shadow {
render() {
p("Thanks for your purchase! You will receive a confirmation email shortly. <br><br> <b>Keep that email; it will be checked at the door.</b>")
.x(50, vw).y(50, vh)
.center()
}
}
register(Success)

View File

@@ -1,21 +0,0 @@
class Why extends Shadow {
render() {
p(`I grew up going to Classical Christian schools all my life. Little did I know, this was a very unique experience - we got to learn all about our history, and everyone had a shared moral understanding.
<br><br>Only when I went out into the world did I realize that most Americans have no idea what this is like. They have never been a part of a shared culture, and the only value they know is multiculturalism.
<br><br>As adults, that is the world the we are all expected to live in.
<br><br>Classical Christian schools are great, but what if I want to live a Classical Christian life?
<br><br>That is what Hyperia is for. It is a Classical Christian space for adults.
<br><br> -- Sam Russell, Founder
`)
.marginTop(window.isMobile() ? 20 : 30, vh)
.marginHorizontal(window.isMobile() ? 10 : 20, vw)
.marginBottom(20, vh)
}
}
register(Why)

View File

@@ -1,68 +0,0 @@
// Create the hover display element
const hoverBox = document.createElement('div');
hoverBox.style.id = "hoverBox"
hoverBox.style.position = 'fixed';
hoverBox.style.padding = '8px 12px';
hoverBox.style.backgroundColor = 'var(--green)';
hoverBox.style.border = '1px solid var(--tan)';
hoverBox.style.color = 'var(--tan)';
hoverBox.style.opacity = '80%';
hoverBox.style.pointerEvents = 'none';
hoverBox.style.zIndex = '9999';
hoverBox.style.fontFamily = 'sans-serif';
hoverBox.style.fontSize = '14px';
hoverBox.style.display = 'none';
document.body.appendChild(hoverBox);
let currentTarget = null;
function capitalizeWords(str) {
return str
.split('-')
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ');
}
function onMouseOver(e) {
const target = e.target;
let paintingName; let artistName;
if(target.id === "back") {
paintingName = "The Garden Terrace"
artistName = "Caspar David Friedrich"
} else if (target.tagName.toLowerCase() === 'img' && target.classList.contains('interactive')) {
const match = target.src.match(/([^\/]+)\.([a-z]{3,4})(\?.*)?$/i); // extract filename
if (!match) return;
const filename = match[1];
const parts = filename.split('_');
if (parts.length !== 2) return;
paintingName = capitalizeWords(parts[0]);
artistName = capitalizeWords(parts[1]);
} else {
return
}
hoverBox.innerHTML = `<strong>${paintingName}</strong><br><span style="font-size: 12px;">${artistName}</span>`;
hoverBox.style.display = 'block';
currentTarget = target;
hoverBox.style.left = `${e.clientX + 15}px`;
hoverBox.style.top = `${e.clientY + 15}px`;
}
function onMouseOut(e) {
if (e.target === currentTarget) {
hoverBox.style.display = 'none';
currentTarget = null;
}
}
function onMouseMove(e) {
if (hoverBox.style.display === 'block') {
hoverBox.style.left = `${e.clientX + 15}px`;
hoverBox.style.top = `${e.clientY + 15}px`;
}
}
document.addEventListener('mouseover', onMouseOver);
document.addEventListener('mouseout', onMouseOut);
document.addEventListener('mousemove', onMouseMove);

View File

@@ -1,56 +0,0 @@
let treeOriginalTop = null;
let currentVelocity = 0;
let isAnimating = false;
window.addEventListener('wheel', (e) => {
if(window.innerWidth < 600) {
return;
}
// Add scroll delta to the velocity
currentVelocity += e.deltaY;
// Start animation loop if not running
if (!isAnimating) {
isAnimating = true;
requestAnimationFrame(animateScroll);
}
}, { passive: false });
function animateScroll() {
const tree = document.getElementById("tree");
if (!treeOriginalTop) {
treeOriginalTop = parseInt(getComputedStyle(tree).top);
}
const treeHeightPX = 0.83 * window.innerHeight;
let treeTopPX = parseInt(getComputedStyle(tree).top);
// Limit per-frame speed (but NOT total speed)
let multiplier = window.innerHeight / 2000;
let delta = Math.max(-100 * multiplier, Math.min(100 * multiplier, currentVelocity));
// Apply the scroll
let newTop = treeTopPX - delta;
// Clamp top/bottom bounds
const maxTop = treeOriginalTop;
const minTop = treeOriginalTop - treeHeightPX;
if (newTop > maxTop) newTop = maxTop;
if (newTop < minTop) newTop = minTop;
tree.style.top = `${newTop}px`;
// Slowly reduce velocity
currentVelocity *= 0.85;
// If velocity is small, stop
if (Math.abs(currentVelocity) > 0.5) {
requestAnimationFrame(animateScroll);
} else {
isAnimating = false;
currentVelocity = 0;
}
}