Implement basic layout of website
This commit is contained in:
parent
eb5283882b
commit
edf97f63dd
|
@ -1,4 +1,5 @@
|
|||
---
|
||||
import { getHighestWeightedLanguage, getLocales } from '../lib/lang/langDriver';
|
||||
interface Props {
|
||||
loggedIn: string;
|
||||
}
|
||||
|
@ -10,20 +11,34 @@ interface NavElement {
|
|||
dropdowns?: Array<NavElement>;
|
||||
}
|
||||
|
||||
const lang = await getLocales(getHighestWeightedLanguage(Astro.request.headers.get('accept-language')), 'navbar');
|
||||
|
||||
const listOfElements: Array<NavElement> = [{
|
||||
title: "home",
|
||||
type: "simple",
|
||||
url: "#"
|
||||
}, {
|
||||
title: "login",
|
||||
title: lang["Link_home"],
|
||||
type: "simple",
|
||||
url: "/"
|
||||
}, {
|
||||
title: "dropdown",
|
||||
title: lang["Link_login"],
|
||||
type: "simple",
|
||||
url: "/login"
|
||||
}, {
|
||||
title: lang["Link_register"],
|
||||
type: "simple",
|
||||
url: "/register"
|
||||
},{
|
||||
title: lang["Link_dropdown"],
|
||||
type: "dropdown",
|
||||
url: "about:blank",
|
||||
dropdowns: [{
|
||||
title: "chuj",
|
||||
title: "drop1",
|
||||
type: "simple",
|
||||
url: "aaa"
|
||||
}, {
|
||||
title: "drop2",
|
||||
type: "simple",
|
||||
url: "aaa"
|
||||
}, {
|
||||
title: "drop3",
|
||||
type: "simple",
|
||||
url: "aaa"
|
||||
}]
|
||||
|
@ -38,8 +53,8 @@ const { loggedIn } = Astro.props;
|
|||
element.type === "dropdown" ?
|
||||
<div class="nav-item nav-item-dropdown">
|
||||
<li><a href={element.url} class="nav-url">{element.title}</a></li>
|
||||
<div>
|
||||
<ul>{element.dropdowns?.map(drop => <li class="nav-item-dropdown-content"><a href={drop.url} class="nav-url">{drop.title}</a></li>)}</ul>
|
||||
<div class="nav-item-dropdown-div">
|
||||
<ul>{element.dropdowns?.map(drop => <li class="nav-item-dropdown-content"><a href={drop.url} class="nav-url">{drop.title}</a></li><br />)}</ul>
|
||||
</div>
|
||||
</div> : // else
|
||||
<li class="nav-item"><a href={element.url} class="nav-url">{element.title}</a></li>
|
||||
|
@ -66,21 +81,37 @@ nav ul {
|
|||
}
|
||||
|
||||
.nav-item:not(:first-child) {
|
||||
margin-left: 20px;
|
||||
color: green;
|
||||
margin-left: 50px;
|
||||
/* color: green; */
|
||||
}
|
||||
|
||||
.nav-item-dropdown {
|
||||
color: red;
|
||||
font-size: xx-large;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.nav-item-dropdown-content {
|
||||
.nav-item-dropdown-div {
|
||||
display: none;
|
||||
position: absolute;
|
||||
background-color: green;
|
||||
}
|
||||
|
||||
.nav-item-dropdown-div ul {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.nav-item-dropdown:hover .nav-item-dropdown-div {
|
||||
display: inline-block;
|
||||
/* position: relative; */
|
||||
/* padding-top: 100px; */
|
||||
}
|
||||
|
||||
.nav-item-dropdown:hover .nav-item-dropdown-content {
|
||||
display: block;
|
||||
z-index: 1;
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
.nav-url {
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
---
|
||||
import Layout from '../../layouts/Layout.astro';
|
||||
import NavBar from '../../components/NavBar.astro';
|
||||
import { getUserResources, updateUserResources } from '../../lib/users';
|
||||
import { getHighestWeightedLanguage, getLocales } from '../../lib/lang/langDriver';
|
||||
|
||||
String.prototype.format = function() {
|
||||
return [...arguments].reduce((p,c) => p.replace(/{}/,c), this);
|
||||
};
|
||||
|
||||
const loggedToken = Astro.cookies.get('sessionToken')?.value ?? null;
|
||||
const username = Astro.cookies.get('username')?.value ?? "";
|
||||
|
||||
if(loggedToken === null || username === "") return Astro.redirect('/');
|
||||
|
||||
const resources = await getUserResources(username);
|
||||
|
||||
await updateUserResources(username, {
|
||||
coal: resources.coal * 2,
|
||||
iron: resources.iron * 3,
|
||||
gold: resources.gold * 4,
|
||||
});
|
||||
|
||||
const langResources = await getLocales(getHighestWeightedLanguage(Astro.request.headers.get('accept-language')), 'resources');
|
||||
const langGame = await getLocales(getHighestWeightedLanguage(Astro.request.headers.get('accept-language')), 'game');
|
||||
// console.log(resources);
|
||||
---
|
||||
|
||||
<Layout title="chujów sto">
|
||||
<NavBar loggedIn="true" />
|
||||
<a href="/logout" style="color: pink;">{langGame['Link_logout']}</a>
|
||||
<h1>{langGame['Header_user'].format(username)}</h1>
|
||||
<ul>
|
||||
<li>{langResources['Label_coal']}: <span id="coal">{resources.coal * 2}</span></li>
|
||||
<li>{langResources['Label_iron']}: <span id="iron">{resources.iron * 3}</span></li>
|
||||
<li>{langResources['Label_gold']}: <span id="gold">{resources.gold * 4}</span></li>
|
||||
</ul>
|
||||
|
||||
<ul>
|
||||
<li><a href="#">{langGame['Link_build'].format('iron mine')}</a></li>
|
||||
<li><a href="#">{langGame['Link_build'].format('gold mine')}</a></li>
|
||||
</ul>
|
||||
</Layout>
|
||||
|
||||
<style>
|
||||
* {
|
||||
color: white;
|
||||
}
|
||||
|
||||
main {
|
||||
margin: auto;
|
||||
padding: 1rem;
|
||||
width: 800px;
|
||||
max-width: calc(100% - 2rem);
|
||||
color: white;
|
||||
font-size: 20px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.astro-a {
|
||||
position: absolute;
|
||||
top: -32px;
|
||||
left: 50%;
|
||||
transform: translatex(-50%);
|
||||
width: 220px;
|
||||
height: auto;
|
||||
z-index: -1;
|
||||
}
|
||||
h1 {
|
||||
font-size: 4rem;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
text-align: center;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
.text-gradient {
|
||||
background-image: var(--accent-gradient);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-size: 400%;
|
||||
background-position: 0%;
|
||||
}
|
||||
.instructions {
|
||||
margin-bottom: 2rem;
|
||||
border: 1px solid rgba(var(--accent-light), 25%);
|
||||
background: linear-gradient(rgba(var(--accent-dark), 66%), rgba(var(--accent-dark), 33%));
|
||||
padding: 1.5rem;
|
||||
border-radius: 8px;
|
||||
}
|
||||
.instructions code {
|
||||
font-size: 0.8em;
|
||||
font-weight: bold;
|
||||
background: rgba(var(--accent-light), 12%);
|
||||
color: rgb(var(--accent-light));
|
||||
border-radius: 4px;
|
||||
padding: 0.3em 0.4em;
|
||||
}
|
||||
.instructions strong {
|
||||
color: rgb(var(--accent-light));
|
||||
}
|
||||
.link-card-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(24ch, 1fr));
|
||||
gap: 2rem;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
|
||||
setInterval(() => {
|
||||
const coal = document.querySelector('#coal');
|
||||
const iron = document.querySelector('#iron');
|
||||
const gold = document.querySelector('#gold');
|
||||
|
||||
if(!coal || !iron || !gold) return;
|
||||
|
||||
coal.innerHTML = String(parseInt(coal?.innerHTML ?? "0") + 1);
|
||||
iron.innerHTML = String(parseInt(iron?.innerHTML ?? "0") + 2);
|
||||
gold.innerHTML = String(parseInt(gold?.innerHTML ?? "0") + 3);
|
||||
}, 1_000)
|
||||
</script>
|
|
@ -1,7 +1,12 @@
|
|||
---
|
||||
import Layout from '../layouts/Layout.astro';
|
||||
import Card from '../components/Card.astro';
|
||||
// import Card from '../components/Card.astro';
|
||||
import NavBar from '../components/NavBar.astro';
|
||||
|
||||
const loggedToken = Astro.cookies.get('sessionToken')?.value ?? null;
|
||||
|
||||
if(loggedToken === null) return Astro.redirect('/login');
|
||||
|
||||
---
|
||||
|
||||
<Layout title="Welcome to Astro.">
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
---
|
||||
import Layout from '../layouts/Layout.astro';
|
||||
import NavBar from '../components/NavBar.astro';
|
||||
|
||||
import { getUserByNickOrEmail } from '../lib/users';
|
||||
|
||||
import { compare } from 'bcrypt';
|
||||
|
||||
let error = "";
|
||||
|
||||
if(Astro.request.method === "POST") {
|
||||
const data = await Astro.request.formData();
|
||||
const username = data.get("username") as string | "";
|
||||
const password = data.get("password") as string | "";
|
||||
|
||||
if(username === "") {
|
||||
error = "username is required";
|
||||
Astro.redirect("/login");
|
||||
}
|
||||
|
||||
if(password === "") {
|
||||
error = "password is required";
|
||||
Astro.redirect("/login");
|
||||
}
|
||||
|
||||
const user = await getUserByNickOrEmail(username as string);
|
||||
|
||||
if(user !== null && await compare(password as string, user.password)) {
|
||||
const sessionTime = import.meta.env.SESSION_TIME_MINUTES * 60;
|
||||
|
||||
const res = await fetch(`${Astro.url.origin}/api/auth/generateAccessToken`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
username,
|
||||
createdFrom: 'loginForm',
|
||||
duration: sessionTime
|
||||
}),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer ' + import.meta.env.MASTER_ACCESSTOKEN
|
||||
}
|
||||
});
|
||||
|
||||
const token = (await res.json()).accessToken;
|
||||
|
||||
Astro.cookies.set("sessionToken", token, {
|
||||
path: "/",
|
||||
maxAge: sessionTime,
|
||||
sameSite: "lax",
|
||||
secure: true
|
||||
});
|
||||
|
||||
Astro.cookies.set("username", username, {
|
||||
path: "/",
|
||||
maxAge: sessionTime,
|
||||
sameSite: "lax",
|
||||
secure: true
|
||||
});
|
||||
|
||||
return Astro.redirect("/game");
|
||||
} else {
|
||||
error = "invalid username or password";
|
||||
return Astro.redirect("/login");
|
||||
}
|
||||
}
|
||||
---
|
||||
|
||||
<Layout title="Login">
|
||||
<NavBar loggedIn="false" />
|
||||
<form method="POST">
|
||||
<input type="text" name="username" placeholder="username" /><br />
|
||||
<input type="password" name="password" placeholder="password" /><br />
|
||||
<input type="submit" value="login" />
|
||||
{ error !== "" ? <p style="color: red;">{error}</p> : "" }
|
||||
</form>
|
||||
</Layout>
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
if(Astro.cookies.has('sessionToken')) {
|
||||
Astro.cookies.delete('sessionToken');
|
||||
}
|
||||
|
||||
return Astro.redirect('/');
|
||||
---
|
|
@ -0,0 +1,122 @@
|
|||
---
|
||||
import Layout from '../layouts/Layout.astro';
|
||||
import NavBar from '../components/NavBar.astro';
|
||||
|
||||
import { createUser } from '../lib/users';
|
||||
import type User from '../types/User';
|
||||
|
||||
import bcrypt from 'bcrypt';
|
||||
|
||||
let error = "";
|
||||
|
||||
if(Astro.request.method === "POST") {
|
||||
const data = await Astro.request.formData();
|
||||
const username = data.get("username") as string | "";
|
||||
const email = data.get("email") as string | "";
|
||||
const password = data.get("password") as string | "";
|
||||
const password2 = data.get("password2") as string | "";
|
||||
|
||||
if(username === "") {
|
||||
error = "username is required";
|
||||
Astro.redirect("/register");
|
||||
}
|
||||
|
||||
if(email === "") {
|
||||
error = "email is required";
|
||||
Astro.redirect("/register");
|
||||
}
|
||||
|
||||
if(password === "") {
|
||||
error = "password is required";
|
||||
Astro.redirect("/register");
|
||||
}
|
||||
|
||||
if(password2 === "") {
|
||||
error = "password2 is required";
|
||||
Astro.redirect("/register");
|
||||
}
|
||||
|
||||
if(username.length < 3 || username.length > 20) {
|
||||
error = "username must be between 3 and 20 characters long";
|
||||
Astro.redirect("/register");
|
||||
}
|
||||
|
||||
if(email.match(/^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/) === null) {
|
||||
error = "email is invalid";
|
||||
Astro.redirect("/register");
|
||||
}
|
||||
|
||||
if(password.length < 8 || password.length > 50) {
|
||||
error = "password must be between 8 and 50 characters long";
|
||||
Astro.redirect("/register");
|
||||
}
|
||||
|
||||
if(password !== password2) {
|
||||
error = "passwords must match";
|
||||
Astro.redirect("/register");
|
||||
}
|
||||
|
||||
if(error === "") {
|
||||
const user: User = {
|
||||
username,
|
||||
email,
|
||||
password: await bcrypt.hash(password, 10),
|
||||
lastLogin: new Date(),
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
resources: {
|
||||
coal: 1,
|
||||
iron: 2,
|
||||
gold: 3
|
||||
}
|
||||
}
|
||||
|
||||
await createUser(user);
|
||||
|
||||
const sessionTime = import.meta.env.SESSION_TIME_MINUTES * 60;
|
||||
|
||||
const res = await fetch(`${Astro.url.origin}/api/auth/generateAccessToken`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
username,
|
||||
createdFrom: 'loginForm',
|
||||
duration: sessionTime
|
||||
}),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer ' + import.meta.env.MASTER_ACCESSTOKEN
|
||||
}
|
||||
});
|
||||
|
||||
const token = (await res.json()).accessToken;
|
||||
|
||||
Astro.cookies.set("sessionToken", token, {
|
||||
path: "/",
|
||||
maxAge: sessionTime,
|
||||
sameSite: "lax",
|
||||
secure: true
|
||||
});
|
||||
|
||||
Astro.cookies.set("username", username, {
|
||||
path: "/",
|
||||
maxAge: sessionTime,
|
||||
sameSite: "lax",
|
||||
secure: true
|
||||
});
|
||||
|
||||
return Astro.redirect("/game");
|
||||
}
|
||||
}
|
||||
---
|
||||
|
||||
<Layout title="Register">
|
||||
<NavBar loggedIn="false" />
|
||||
<form method="POST">
|
||||
<input type="text" name="username" placeholder="username" /><br />
|
||||
<input type="email" name="email" placeholder="email" /><br />
|
||||
<input type="password" name="password" placeholder="password" /><br />
|
||||
<input type="password" name="password2" placeholder="password2" /><br />
|
||||
<input type="submit" value="register" />
|
||||
{ error !== "" ? <p style="color: red;">{error}</p> : "" }
|
||||
</form>
|
||||
</Layout>
|
Loading…
Reference in New Issue