Compare commits
10 Commits
8dcd74e2e9
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 7345878f10 | |||
| 5569f35037 | |||
| 8965ffc11b | |||
| aa0c82e6d3 | |||
| 0a488a80ce | |||
| a12b65a488 | |||
| 1d539e44ac | |||
| 9ade18903c | |||
| 4acd9d0245 | |||
| f9c8fde7f3 |
20
public/api.js
Normal file
20
public/api.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
export async function authFetch(url, options = {}) {
|
||||||
|
const token = localStorage.getItem("token"); // hol den JWT aus localStorage
|
||||||
|
|
||||||
|
// füge Headers zusammen: Content‑Type + Bearer Token
|
||||||
|
const headers = {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
...options.headers, // vorhandene Header nicht überschreiben
|
||||||
|
...(token ? {
|
||||||
|
"Authorization": `Bearer ${token}` // 🔑 wichtiger Header
|
||||||
|
} : {})
|
||||||
|
};
|
||||||
|
|
||||||
|
// führe den fetch aus mit dem zusammengebauten Header
|
||||||
|
const response = await fetch(url, {
|
||||||
|
...options,
|
||||||
|
headers
|
||||||
|
});
|
||||||
|
|
||||||
|
return response; // response weitergeben
|
||||||
|
}
|
||||||
@@ -2,29 +2,46 @@
|
|||||||
<html lang="de">
|
<html lang="de">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Dashboard</title>
|
<title>Dashboard - Schic App</title>
|
||||||
<link rel="stylesheet" href="styles.css">
|
<link rel="stylesheet" href="styles.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1>Dashboard</h1>
|
<h1>Dashboard</h1>
|
||||||
<p>Login erfolgreich 🎉</p>
|
<p id="welcomeMessage">Willkommen …</p>
|
||||||
<button id="logout">Logout</button>
|
|
||||||
|
<button id="logoutBtn">Logout</button>
|
||||||
|
|
||||||
|
<h2>Elemente</h2>
|
||||||
|
|
||||||
|
<div class="elements-container">
|
||||||
|
<div class="filters">
|
||||||
|
<label>Inhalt: <input type="text" id="filter-inhalt" placeholder="Filter Inhalt"></label>
|
||||||
|
<label>Bundesland: <input type="text" id="filter-bundesland" placeholder="Filter Bundesland"></label>
|
||||||
|
<label>Fach: <input type="text" id="filter-fach" placeholder="Filter Fach"></label>
|
||||||
|
<label>Version: <input type="text" id="filter-version" placeholder="Filter Version"></label>
|
||||||
|
<label>Stufe: <input type="number" id="filter-stufe" placeholder="Stufe"></label>
|
||||||
|
<button id="clear-filters">Filter zurücksetzen</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<table id="elementsTable">
|
||||||
const token = localStorage.getItem("token")
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Inhalt</th>
|
||||||
|
<th>Bundesland</th>
|
||||||
|
<th>Fach</th>
|
||||||
|
<th>Version</th>
|
||||||
|
<th>Stufe</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="elementsTbody"></tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
if (!token) {
|
<p id="errorMessage" class="error"></p>
|
||||||
window.location.href = "login.html"
|
</div>
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById("logout").addEventListener("click", () => {
|
|
||||||
localStorage.removeItem("token")
|
|
||||||
window.location.href = "login.html"
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
<!-- Lade das Dashboard‑Script -->
|
||||||
|
<script type="module" src="dashboard.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
157
public/dashboard.js
Normal file
157
public/dashboard.js
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
// dashboard.js
|
||||||
|
import { authFetch } from "./api.js";
|
||||||
|
|
||||||
|
// Element‑Referenzen
|
||||||
|
const welcomeMessage = document.getElementById("welcomeMessage");
|
||||||
|
const todosList = document.getElementById("todosList");
|
||||||
|
const errorMessage = document.getElementById("errorMessage");
|
||||||
|
const logoutBtn = document.getElementById("logoutBtn");
|
||||||
|
// Elements UI
|
||||||
|
const elementsTbody = document.getElementById("elementsTbody");
|
||||||
|
const filterInhalt = document.getElementById("filter-inhalt");
|
||||||
|
const filterBundesland = document.getElementById("filter-bundesland");
|
||||||
|
const filterFach = document.getElementById("filter-fach");
|
||||||
|
const filterVersion = document.getElementById("filter-version");
|
||||||
|
const filterStufe = document.getElementById("filter-stufe");
|
||||||
|
const clearFiltersBtn = document.getElementById("clear-filters");
|
||||||
|
|
||||||
|
// Prüfe ob ein JWT vorhanden ist
|
||||||
|
const token = localStorage.getItem("token");
|
||||||
|
if (!token) {
|
||||||
|
// Kein Token → zurück zur Login‑Seite
|
||||||
|
window.location.href = "login.html";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logout – Token löschen + weiterleiten
|
||||||
|
logoutBtn.addEventListener("click", () => {
|
||||||
|
localStorage.removeItem("token");
|
||||||
|
window.location.href = "login.html";
|
||||||
|
});
|
||||||
|
|
||||||
|
// Lade Todos vom Backend
|
||||||
|
async function loadElements() {
|
||||||
|
try {
|
||||||
|
const res = await authFetch("/elements"); // fetchWrapper sendet den JWT Automatisch
|
||||||
|
if (!res.ok) {
|
||||||
|
errorMessage.textContent = "Fehler beim Abrufen der Elemente!";
|
||||||
|
if (res.status === 401) {
|
||||||
|
window.location.href = "login.html"; // bei ungültigem Token zum Login
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const todos = await res.json();
|
||||||
|
|
||||||
|
// Zeige Todos in der Liste an
|
||||||
|
todosList.innerHTML = "";
|
||||||
|
if (Array.isArray(todos) && todos.length > 0) {
|
||||||
|
todos.forEach(todo => {
|
||||||
|
const li = document.createElement("li");
|
||||||
|
li.textContent = todo.task;
|
||||||
|
todosList.appendChild(li);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
todosList.innerHTML = "<li>Keine Todos gefunden.</li>";
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Fetch Error:", err);
|
||||||
|
errorMessage.textContent = "Serverfehler beim Laden!";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Willkommenstext setzen
|
||||||
|
welcomeMessage.textContent = "Willkommen zum Dashboard!";
|
||||||
|
|
||||||
|
// Rufe die Todos ab
|
||||||
|
loadTodos();
|
||||||
|
|
||||||
|
// ---------- Elements: load, render, filter ----------
|
||||||
|
let elementsData = [];
|
||||||
|
|
||||||
|
async function loadElements() {
|
||||||
|
try {
|
||||||
|
// Route: mounted at /elements + route '/elements' => /elements/elements
|
||||||
|
const res = await authFetch('/elements/elements');
|
||||||
|
if (!res.ok) {
|
||||||
|
errorMessage.textContent = 'Fehler beim Abrufen der Elemente.';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
elementsData = await res.json();
|
||||||
|
renderElements(elementsData);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Elements Fetch Error:', err);
|
||||||
|
errorMessage.textContent = 'Serverfehler beim Laden der Elemente.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderElements(data) {
|
||||||
|
if (!elementsTbody) return;
|
||||||
|
elementsTbody.innerHTML = '';
|
||||||
|
|
||||||
|
if (!Array.isArray(data) || data.length === 0) {
|
||||||
|
const tr = document.createElement('tr');
|
||||||
|
const td = document.createElement('td');
|
||||||
|
td.colSpan = 5;
|
||||||
|
td.textContent = 'Keine Elemente gefunden.';
|
||||||
|
tr.appendChild(td);
|
||||||
|
elementsTbody.appendChild(tr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.forEach(el => {
|
||||||
|
const tr = document.createElement('tr');
|
||||||
|
// Skip id; show columns in schema order
|
||||||
|
const cols = [el.inhalt, el.bundesland, el.fach, el.version, el.stufe];
|
||||||
|
cols.forEach(val => {
|
||||||
|
const td = document.createElement('td');
|
||||||
|
td.textContent = (val === null || val === undefined) ? '' : String(val);
|
||||||
|
tr.appendChild(td);
|
||||||
|
});
|
||||||
|
elementsTbody.appendChild(tr);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyFilters() {
|
||||||
|
if (!elementsData || elementsData.length === 0) return renderElements([]);
|
||||||
|
|
||||||
|
const fInhalt = (filterInhalt?.value || '').trim().toLowerCase();
|
||||||
|
const fBund = (filterBundesland?.value || '').trim().toLowerCase();
|
||||||
|
const fFach = (filterFach?.value || '').trim().toLowerCase();
|
||||||
|
const fVer = (filterVersion?.value || '').trim().toLowerCase();
|
||||||
|
const fSt = (filterStufe?.value || '').trim();
|
||||||
|
|
||||||
|
const filtered = elementsData.filter(el => {
|
||||||
|
if (fInhalt && !(el.inhalt || '').toLowerCase().includes(fInhalt)) return false;
|
||||||
|
if (fBund && !(el.bundesland || '').toLowerCase().includes(fBund)) return false;
|
||||||
|
if (fFach && !(el.fach || '').toLowerCase().includes(fFach)) return false;
|
||||||
|
if (fVer && !(el.version || '').toLowerCase().includes(fVer)) return false;
|
||||||
|
if (fSt) {
|
||||||
|
const n = Number(fSt);
|
||||||
|
if (!Number.isNaN(n)) {
|
||||||
|
if (Number(el.stufe) !== n) return false;
|
||||||
|
} else {
|
||||||
|
// non-numeric filter: substring match
|
||||||
|
if (!(String(el.stufe) || '').includes(fSt)) return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
renderElements(filtered);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupFilterListeners() {
|
||||||
|
const inputs = [filterInhalt, filterBundesland, filterFach, filterVersion, filterStufe];
|
||||||
|
inputs.forEach(i => { if (i) i.addEventListener('input', applyFilters); });
|
||||||
|
if (clearFiltersBtn) clearFiltersBtn.addEventListener('click', () => {
|
||||||
|
inputs.forEach(i => { if (i) i.value = ''; });
|
||||||
|
applyFilters();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize elements UI if present
|
||||||
|
if (elementsTbody) {
|
||||||
|
setupFilterListeners();
|
||||||
|
loadElements();
|
||||||
|
}
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
<p id="errorMessage" class="error"></p>
|
<p id="errorMessage" class="error"></p>
|
||||||
|
|
||||||
<a href="index.html">Zurück zur Startseite</a>
|
<a href="index.html">Zurück zur Startseite</a>
|
||||||
|
<a href="register.html">Noch keinen Account? Registrieren</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="login.js"></script>
|
<script src="login.js"></script>
|
||||||
|
|||||||
@@ -1,40 +1,31 @@
|
|||||||
const form = document.getElementById("loginForm")
|
const form = document.getElementById("loginForm");
|
||||||
const errorMessage = document.getElementById("errorMessage")
|
const errorMessage = document.getElementById("errorMessage");
|
||||||
if (form) {
|
|
||||||
console.log("LoginForm gefunden")
|
|
||||||
} else {
|
|
||||||
console.log("LoginForm NICHT gefunden")
|
|
||||||
}
|
|
||||||
|
|
||||||
form.addEventListener("submit", async (event) => {
|
form.addEventListener("submit", async (event) => {
|
||||||
event.preventDefault()
|
event.preventDefault();
|
||||||
|
|
||||||
const username = document.getElementById("username").value
|
const username = document.getElementById("username").value;
|
||||||
const password = document.getElementById("password").value
|
const password = document.getElementById("password").value;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch("/auth/login", {
|
const response = await fetch("/auth/login", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: { "Content-Type": "application/json" },
|
||||||
"Content-Type": "application/json"
|
|
||||||
},
|
|
||||||
body: JSON.stringify({ username, password })
|
body: JSON.stringify({ username, password })
|
||||||
})
|
});
|
||||||
|
|
||||||
const data = await response.json()
|
const data = await response.json();
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
errorMessage.textContent = data.message
|
errorMessage.textContent = data?.message || "Login fehlgeschlagen";
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Token speichern
|
localStorage.setItem("token", data.token);
|
||||||
localStorage.setItem("token", data.token)
|
window.location.href = "dashboard.html";
|
||||||
|
|
||||||
// Weiterleiten
|
|
||||||
window.location.href = "dashboard.html"
|
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
errorMessage.textContent = "Server nicht erreichbar"
|
console.error("Login Error:", err);
|
||||||
|
errorMessage.textContent = "Server nicht erreichbar";
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
27
public/register.html
Normal file
27
public/register.html
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Login - Schic-App</title>
|
||||||
|
<link rel="stylesheet" href="styles.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<h1>Registrieren</h1>
|
||||||
|
|
||||||
|
<form id="registerForm">
|
||||||
|
<input type="text" id="username" placeholder="Username" required>
|
||||||
|
<input type="password" id="password" placeholder="Passwort" required>
|
||||||
|
<button type="submit">Registrieren</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<p id="errorMessage" class="error"></p>
|
||||||
|
|
||||||
|
<a href="index.html">Zurück zur Startseite</a>
|
||||||
|
<a href="login.html">Bereits registriert? Einloggen</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="register.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
35
public/register.js
Normal file
35
public/register.js
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
const form = document.getElementById("registerForm");
|
||||||
|
const errorMessage = document.getElementById("errorMessage");
|
||||||
|
|
||||||
|
if (!form) {
|
||||||
|
console.error('Register form not found in DOM');
|
||||||
|
} else {
|
||||||
|
form.addEventListener("submit", async (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const username = document.getElementById("username").value;
|
||||||
|
const password = document.getElementById("password").value;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch("/auth/register", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ username, password })
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
errorMessage.textContent = data?.message || "Registrierung fehlgeschlagen";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
localStorage.setItem("token", data.token);
|
||||||
|
window.location.href = "dashboard.html";
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Register Error:", err);
|
||||||
|
errorMessage.textContent = "Server nicht erreichbar";
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,12 +1,14 @@
|
|||||||
import jwt from 'jsonwebtoken'
|
import jwt from 'jsonwebtoken'
|
||||||
|
|
||||||
function authMiddleware(req, res, next) {
|
function authMiddleware(req, res, next) {
|
||||||
const token = req.headers['authorization']
|
const authHeader = req.headers['authorization'] || req.headers['Authorization'];
|
||||||
|
if (!authHeader) { return res.status(401).json({ message: "No token provided" }) }
|
||||||
|
|
||||||
if (!token) { return res.status(401).json({ message: "No token provided" }) }
|
// support headers in the form: 'Bearer <token>' or just the token
|
||||||
|
const token = authHeader.startsWith('Bearer ') ? authHeader.split(' ')[1] : authHeader;
|
||||||
|
|
||||||
jwt.verify(token, process.env.JWT_SECRET, (err, decoded) => {
|
jwt.verify(token, process.env.JWT_SECRET, (err, decoded) => {
|
||||||
if(err) {return res.status(401).json({message: "Invalid token"})}
|
if (err) { return res.status(401).json({ message: "Invalid token" }) }
|
||||||
req.userId = decoded.id
|
req.userId = decoded.id
|
||||||
next()
|
next()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ router.post('/login', async (req, res) => {
|
|||||||
const { username, password } = req.body
|
const { username, password } = req.body
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const user = await prisma.users.findUnique({
|
const user = await prisma.user.findUnique({
|
||||||
where: {
|
where: {
|
||||||
username: username
|
username: username
|
||||||
}
|
}
|
||||||
@@ -59,9 +59,9 @@ router.post('/login', async (req, res) => {
|
|||||||
const token = jwt.sign({ id: user.id }, process.env.JWT_SECRET, { expiresIn: '24h' })
|
const token = jwt.sign({ id: user.id }, process.env.JWT_SECRET, { expiresIn: '24h' })
|
||||||
res.json({ token })
|
res.json({ token })
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err.message)
|
console.error("LOGIN ROUTE ERROR:", err);
|
||||||
res.sendStatus(503)
|
res.status(500).json({ message: "Server error during login" });
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import prisma from '../prismaClient.js'
|
|||||||
|
|
||||||
const router = express.Router()
|
const router = express.Router()
|
||||||
|
|
||||||
router.get('/', async (req, res) => {
|
router.get('/elements', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
// Versuche es mit exakt dem Namen aus deinem Schema (Elements)
|
// Versuche es mit exakt dem Namen aus deinem Schema (Elements)
|
||||||
const data = await prisma.Elements.findMany();
|
const data = await prisma.Elements.findMany();
|
||||||
|
|||||||
Reference in New Issue
Block a user