Login System + API

This commit is contained in:
Moritz Utcke
2023-03-25 19:51:35 +04:00
parent dcbdf0e8d0
commit 5285f832bf
19 changed files with 551 additions and 7 deletions

34
src/pages/api/login.ts Normal file
View File

@@ -0,0 +1,34 @@
import type { APIRoute } from "astro";
import { success, MissingPropertyError, MissingEntityError, ActionFailedError, InvalidDataError, error } from "../../lib/APIResponse";
import { validatePassword } from "../../lib/Password";
import { User } from "../../lib/User";
import moment from "moment";
import { encodeToken } from "../../lib/JsonWebToken";
/**
* Ruft einen Nutzer anhand seiner uid aus der Datenbank ab.
* @param param0 Die Request mit dem request body. Dieser enthält entweder eine uid mit der der Benutzer identifiziert werden kann.
*/
export const post: APIRoute = async ({ request }) => {
const body = await request.json();
if (!body.hasOwnProperty("username") || !body.hasOwnProperty("password")) {
return MissingPropertyError(["username", "password"]);
}
const user = await User.fromUsername(body.username);
if (!user) {
return error(["Invalid username or password."]);
}
// Validate Password
if (!validatePassword(user.password, body.password)) {
return error(["Invalid username or password."]);
}
const expiry = moment().add(2, "days").unix();
const token = encodeToken({ id: user.id, uid: user.uid, exp: expiry })
return success({ token, expires: expiry });
}

43
src/pages/api/user.ts Normal file
View File

@@ -0,0 +1,43 @@
import type { APIRoute } from "astro";
import { success, MissingPropertyError, MissingEntityError, ActionFailedError, InvalidDataError } from "../../lib/APIResponse";
import { User } from "../../lib/User";
import { UserRegisterValidator, UserType, UserTypeValidator } from "../../lib/User/type";
/**
* Ruft einen Nutzer anhand seiner uid aus der Datenbank ab.
* @param param0 Die Request mit dem request body. Dieser enthält entweder eine uid mit der der Benutzer identifiziert werden kann.
*/
export const get: APIRoute = async ({ request }) => {
const body = await request.json();
if (!body.hasOwnProperty("uid")) {
return MissingPropertyError(["uid"]);
}
const user = User.fromPublicId(body.uid);
if (!user) {
return MissingEntityError("user");
}
return success(user);
}
export const put: APIRoute = async ({ request }) => {
const body = await request.json();
const validate = UserRegisterValidator.safeParse(body);
if (validate.success == false) {
return InvalidDataError(validate.error);
}
const result = await User.create(body as UserType);
if (!result) {
return ActionFailedError();
}
return success({ uid: result.uid, id: result.id });
}

17
src/pages/login.astro Normal file
View File

@@ -0,0 +1,17 @@
---
import moment from "moment";
import LoginView from "../components/LoginView.svelte";
import Layout from "../layouts/Layout.astro";
const token = Astro.cookies.get("token").value;
const expires = Astro.cookies.get("expires").number();
const now = moment().unix();
if (token && now < expires) {
return Astro.redirect("/user");
}
---
<Layout title="Login">
<LoginView client:only></LoginView>
</Layout>

17
src/pages/signup.astro Normal file
View File

@@ -0,0 +1,17 @@
---
import moment from "moment";
import RegisterView from "../components/RegisterView.svelte";
import Layout from "../layouts/Layout.astro";
const token = Astro.cookies.get("token").value;
const expires = Astro.cookies.get("expires").number();
const now = moment().unix();
if (token && now < expires) {
return Astro.redirect("/user");
}
---
<Layout title="Login">
<RegisterView client:only></RegisterView>
</Layout>

View File

@@ -0,0 +1,63 @@
---
import moment from "moment";
import Layout from "../../layouts/Layout.astro"
import { decodeToken } from "../../lib/JsonWebToken";
import { User } from "../../lib/User";
const token = Astro.cookies.get("token").value;
const expires = Astro.cookies.get("expires").number();
const now = moment().unix();
if (!token || now > expires) {
Astro.cookies.delete("token");
Astro.cookies.delete("expires");
return Astro.redirect("/login");
}
const parsed = decodeToken(token);
const user = await User.fromPublicId(parsed.uid);
if (!user) {
return Astro.redirect("/login");
}
---
<Layout title="Dashboard">
<div class="profile_container">
<div><h1>Willkommen zurück <b>{user.username}</b></h1>
<div id="searchBoxContainer" class="flex-row" style="gap: 5px; margin: 10px 0;"></div>
</div>
<div class="flex-column" style="gap: 10px;">
<a href="/user/settings" class="weiterbutton">Account Details Ändern</a>
<a href="/logout" class="weiterbutton">Ausloggen</a>
</div>
<div class="flex-row filter-bar" style="grid-area: 2/1/2/3; min-height: 0; gap: 20px;">
<div class="flex-row" style="width: 100%;">
<a onclick="addSort()" class='weiterbutton'>Filter Hinzufügen</a>
<a onclick="window.Offset > 0 ? (() => {
GetData(window.Offset - 1)
})() : null" class='weiterbutton'>Vorherige Seite</a>
<a onclick="window.Offset >= 0 ? (() => {
GetData(window.Offset + 1)
})() : null" class='weiterbutton'>Nächste Seite</a>
<a onclick="window.Offset > 0 ? (() => {
GetData(0)
})() : null" class='weiterbutton'>Erste Seite</a>
</div>
<div class="flex-row" style="width: 100%;">
<select onchange="window.GetLength = parseInt(this.value); GetData(0);">
<option value="25">25</option>
<option value="50">50</option>
<option value="75">75</option>
<option value="100">100</option>
</select>
<select onchange="window.orderDirection = this.value; GetData(0);">
<option value="DESC">Absteigend</option>
<option value="ASC">Aufsteigend</option>
</select>
</div>
</div>
<div class='login-container' id="items-container"></div>
</div>
</Layout>