From 241ebe07f26e650ba73da3fa9e0b9ed8d8d0154b Mon Sep 17 00:00:00 2001 From: Janik Date: Sun, 20 Jul 2025 14:45:16 +0200 Subject: [PATCH] Working login, but session not implemented yet --- client/src/App.css | 22 +++++++ client/src/Home.tsx | 13 +++-- client/src/main.tsx | 5 ++ client/src/pages/admin.tsx | 40 +++++++++++++ client/src/pages/copylink.tsx | 11 ++-- client/src/pages/login.tsx | 59 ++++++++++++++++--- server/index.js | 67 +++++++++++++++++++-- server/package-lock.json | 106 ++++++++++++++++++++++++++++++++++ server/package.json | 2 + 9 files changed, 304 insertions(+), 21 deletions(-) create mode 100644 client/src/pages/admin.tsx diff --git a/client/src/App.css b/client/src/App.css index c51f84a..a4f5a35 100644 --- a/client/src/App.css +++ b/client/src/App.css @@ -149,4 +149,26 @@ li { } #oriurl { margin: 50px; +} +#shorturl { + margin: 50px; +} + + +#button_copy { + height: 100px; + width: 300px; + font-size: x-large; + border-radius: 15px; + will-change: filter; + transition: filter 400ms; +} +#button_copy:hover { + filter: drop-shadow(0 0 0.5em #f29732); + border-color: transparent; + transition: 0.3s; +} +#button_copy:active { + border-color: transparent; + background-color: transparent; } \ No newline at end of file diff --git a/client/src/Home.tsx b/client/src/Home.tsx index e46a94e..a5c7008 100644 --- a/client/src/Home.tsx +++ b/client/src/Home.tsx @@ -8,18 +8,18 @@ const Home = () => {
- + trx shortlink logo

trx shortlink

@@ -38,8 +38,11 @@ const Home = () => { export default Home; async function shortenLink(url: string) { - if ((document.getElementById("oriurl") as HTMLInputElement).value == "") { + const inputi = (document.getElementById("oriurl") as HTMLInputElement).value; + if (inputi == "") { alert("The url cannot be empty!") + } else if(!(inputi.startsWith("https://") || inputi.startsWith("http://"))) { + alert("Please provide a valid url!") } else { const res = await fetch("http://localhost:3000/api/shorten", { method: "POST", @@ -49,7 +52,7 @@ async function shortenLink(url: string) { const data = await res.json(); console.log(data.short_url); (document.getElementById("oriurl") as HTMLInputElement).value = "" - location.href = "link/" + data.id; + location.href = "/link/" + data.id; return data.id; } } diff --git a/client/src/main.tsx b/client/src/main.tsx index 4de0d8a..f3d8317 100644 --- a/client/src/main.tsx +++ b/client/src/main.tsx @@ -3,6 +3,7 @@ import './index.css'; import Home from './Home.tsx'; import Login from './pages/login.tsx' import Copylink from './pages/copylink.tsx'; +import Admin from './pages/admin.tsx'; import { BrowserRouter as Router, @@ -22,6 +23,10 @@ createRoot(document.getElementById('root')!).render( path='/login' element={} /> + } + /> } diff --git a/client/src/pages/admin.tsx b/client/src/pages/admin.tsx new file mode 100644 index 0000000..7611ab5 --- /dev/null +++ b/client/src/pages/admin.tsx @@ -0,0 +1,40 @@ +import shortlinkLogo from '../assets/trx-shortlink-orange.png' +import '../App.css' + +const Admin = () => { + return ( + <> +
+
+ +
+
+
+ + trx shortlink logo + +

trx shortlink

+
+
+

Admin Panel

+ + + +
+ + ); +}; + +export default Admin; + + diff --git a/client/src/pages/copylink.tsx b/client/src/pages/copylink.tsx index 103be30..ef9eb86 100644 --- a/client/src/pages/copylink.tsx +++ b/client/src/pages/copylink.tsx @@ -1,8 +1,11 @@ import shortlinkLogo from '../assets/trx-shortlink-orange.png' import '../App.css' +import { useParams } from 'react-router-dom'; const Copylink = () => { + const { id } = useParams<{ id: string }>(); + return ( <>
@@ -14,20 +17,20 @@ const Copylink = () => {
- + trx shortlink logo

trx shortlink

- -
diff --git a/client/src/pages/login.tsx b/client/src/pages/login.tsx index d99283a..d7c916e 100644 --- a/client/src/pages/login.tsx +++ b/client/src/pages/login.tsx @@ -1,8 +1,39 @@ import shortlinkLogo from '../assets/trx-shortlink-orange.png' import '../App.css' +import { useState } from "react"; -function App() { + +function Login() { + const [username, setUsername] = useState(""); + const [password, setPassword] = useState(""); + const [error, setError] = useState(""); + + const handleLogin = async () => { + try { + const res = await fetch("http://localhost:3000/api/login", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + username: (document.getElementById("username")as HTMLInputElement).value, + password: (document.getElementById("password")as HTMLInputElement).value + }), + credentials: "include", // wichtig für Cookies (Session) + }); + + const data = await res.json(); + if (res.ok) { + // Login erfolgreich + console.log("Eingeloggt!"); + location.href = "/admin" + } else { + setError(data.message || "Login fehlgeschlagen"); + } + } catch (err) { + setError("Serverfehler beim Login \n\n\n" + err); + + } + }; return ( <> @@ -21,22 +52,34 @@ function App() {
- + trx shortlink logo

trx shortlink

Login

-

-

- + setUsername(e.target.value)} + />
+ setPassword(e.target.value)} + />
+ + {error &&

{error}

}
) } -export default App +export default Login; diff --git a/server/index.js b/server/index.js index 484f013..e765526 100644 --- a/server/index.js +++ b/server/index.js @@ -2,12 +2,33 @@ const express = require("express"); const cors = require("cors"); const sqlite3 = require("sqlite3").verbose(); const app = express(); +const session = require('express-session'); require("dotenv").config(); -app.use(cors()); +app.use(cors({ + origin: "http://localhost:5173", // genau die Adresse deines Frontends + credentials: true // Cookies erlauben +})); app.use(express.json()); - +const bcrypt = require('bcrypt'); const db = new sqlite3.Database("../db.sqlite"); +db.prepare(`CREATE TABLE IF NOT EXISTS users ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + username TEXT UNIQUE, + password_hash TEXT)`).run(); +const adminExists = db.get("SELECT * FROM users WHERE username = ?", ["admin"]); +if (!adminExists) { + const hash = bcrypt.hashSync("admin", 10); // Passwort: admin + db.prepare(`INSERT INTO users (username, password_hash) VALUES (?, ?)`).run('admin', hash); +} + +app.use(session({ + secret: 'supergeheim', // Ändern in echte Secret + resave: false, + saveUninitialized: false, + cookie: { secure: false } +})); + // Beispiel-Route: Link erstellen app.post("/api/shorten", (req, res) => { @@ -18,8 +39,8 @@ app.post("/api/shorten", (req, res) => { [id, original_url], (err) => { if (err) return res.status(500).json({ error: err.message }); - res.json({ short_url: `http://localhost:3000/${id}` }); - res.json({ id: `${id}`}); + res.json({ short_url: `http://localhost:3000/${id}`, + id: `${id}` }); } ); }); @@ -33,4 +54,42 @@ app.get("/:id", (req, res) => { }); }); + +app.post('/api/login', (req, res) => { + const { username, password } = req.body; + + db.get("SELECT * FROM users WHERE username = ?", [username], (err, user) => { + if (err) { + console.error("Datenbankfehler:", err); + return res.status(500).json({ message: 'Interner Serverfehler' }); + } + + if (!user) { + return res.status(401).json({ message: 'Benutzer existiert nicht' }); + } + + //console.log("Klartext-Passwort:", password); + //console.log("Hash aus DB:", user.password_hash); + + const valid = bcrypt.compareSync(password, user.password_hash); + if (!valid) return res.status(401).json({ message: 'Falsches Passwort' }); + + req.session.user = { id: user.id, username: user.username }; + res.json({ success: true }); + }); +}); + +app.get('/api/check-auth', (req, res) => { + res.json({ loggedIn: !!req.session.user }); +}); + +app.post('/api/logout', (req, res) => { + req.session.destroy(() => res.json({ success: true })); +}); + + + + + + app.listen(3000, () => console.log("Server läuft auf http://localhost:3000")); diff --git a/server/package-lock.json b/server/package-lock.json index ed849eb..3d61142 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -9,9 +9,11 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "bcrypt": "^6.0.0", "cors": "^2.8.5", "dotenv": "^17.2.0", "express": "^5.1.0", + "express-session": "^1.18.2", "sqlite3": "^5.1.7" } }, @@ -177,6 +179,29 @@ ], "license": "MIT" }, + "node_modules/bcrypt": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-6.0.0.tgz", + "integrity": "sha512-cU8v/EGSrnH+HnxV2z0J7/blxH8gq7Xh2JFT6Aroax7UohdmiJJlxApMxtKfuI7z68NvvVcmR78k2LbT6efhRg==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^8.3.0", + "node-gyp-build": "^4.8.4" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/bcrypt/node_modules/node-addon-api": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.5.0.tgz", + "integrity": "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==", + "license": "MIT", + "engines": { + "node": "^18 || ^20 || >= 21" + } + }, "node_modules/bindings": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", @@ -661,6 +686,46 @@ "url": "https://opencollective.com/express" } }, + "node_modules/express-session": { + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.2.tgz", + "integrity": "sha512-SZjssGQC7TzTs9rpPDuUrR23GNZ9+2+IkA/+IJWmvQilTr5OSliEHGF+D9scbIpdC6yGtTI0/VhaHoVes2AN/A==", + "license": "MIT", + "dependencies": { + "cookie": "0.7.2", + "cookie-signature": "1.0.7", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-headers": "~1.1.0", + "parseurl": "~1.3.3", + "safe-buffer": "5.2.1", + "uid-safe": "~2.1.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/express-session/node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "license": "MIT" + }, + "node_modules/express-session/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express-session/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, "node_modules/file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", @@ -1399,6 +1464,17 @@ "node": ">= 10.12.0" } }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "license": "MIT", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, "node_modules/nopt": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", @@ -1465,6 +1541,15 @@ "node": ">= 0.8" } }, + "node_modules/on-headers": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -1603,6 +1688,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/random-bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", + "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -2146,6 +2240,18 @@ "node": ">= 0.6" } }, + "node_modules/uid-safe": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", + "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", + "license": "MIT", + "dependencies": { + "random-bytes": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/unique-filename": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", diff --git a/server/package.json b/server/package.json index 0bde787..1c2e8a8 100644 --- a/server/package.json +++ b/server/package.json @@ -11,9 +11,11 @@ "license": "ISC", "type": "commonjs", "dependencies": { + "bcrypt": "^6.0.0", "cors": "^2.8.5", "dotenv": "^17.2.0", "express": "^5.1.0", + "express-session": "^1.18.2", "sqlite3": "^5.1.7" } }