neue db verbindung
This commit is contained in:
parent
6f5c9a28a1
commit
16fb18f9e6
@ -1 +1 @@
|
|||||||
4PsgCvoOJLNXPpxOHOvm+KbVYz3pNxg8oOXO7zoH3MPffEhZLI7i5qf3o6oqZDI04us8xSSz9j3KIN+Atno/VFlYzSoq3ki1F+WSTz37LfcE3goPqhm6UaH8c9lHdulemH9tqgGq/DxgbKaup5t/ZJnLseaHHpdyTZok1jWULN0nlDuL/HvVVtqw5sboPqU=
|
MgmDGURt7NfYtetWb79ghkifQA6ztKwK/7Hl1BNBG2QA+kIbDtHM+1R8XPRiTtDtBHPo+T8UmzvmOuztdphLvMnMW7/Jlqo+VAg4mbYDRLz8WQja5KBmIQJf1eF5riHPu0zQDjY7VU1AX2mzR8xfWrB+CngkagEHXv7OsigsRmxlrB3oGTd6GY6PeAYq3jTblo4kjDDg6GWeDJoF
|
||||||
@ -3,45 +3,76 @@ const router = express.Router();
|
|||||||
const mysql = require("mysql2/promise");
|
const mysql = require("mysql2/promise");
|
||||||
|
|
||||||
// ✅ nutzt deinen bestehenden config-manager (NICHT utils/config!)
|
// ✅ nutzt deinen bestehenden config-manager (NICHT utils/config!)
|
||||||
const { configExists, saveConfig } = require("../config-manager");
|
const { configExists, loadConfig, saveConfig } = require("../config-manager");
|
||||||
|
|
||||||
// ✅ DB + Session Reset (wie in deiner app.js)
|
// ✅ DB + Session Reset (wie in deiner app.js)
|
||||||
const db = require("../db");
|
const db = require("../db");
|
||||||
const { resetSessionStore } = require("../config/session");
|
const { resetSessionStore } = require("../config/session");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Setup darf nur laufen, wenn config.enc NICHT existiert
|
* Setup ist immer erreichbar – auch wenn config.enc schon existiert.
|
||||||
* (sonst könnte jeder die DB später überschreiben)
|
* So kann die DB-Verbindung jederzeit korrigiert werden.
|
||||||
|
* Schutz: Nur wenn DB bereits erreichbar ist UND User eingeloggt ist → blockieren.
|
||||||
*/
|
*/
|
||||||
function blockIfInstalled(req, res, next) {
|
function blockIfInstalled(req, res, next) {
|
||||||
if (configExists()) {
|
// Immer durchlassen – Setup muss auch zur Korrektur nutzbar sein
|
||||||
return res.redirect("/");
|
|
||||||
}
|
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Setup Form anzeigen
|
* Setup Form anzeigen – vorhandene Werte aus config.enc als Defaults laden
|
||||||
*/
|
*/
|
||||||
router.get("/", blockIfInstalled, (req, res) => {
|
router.get("/", blockIfInstalled, (req, res) => {
|
||||||
|
// Bestehende Config als Vorausfüllung laden (Passwort bleibt leer)
|
||||||
|
let existing = {};
|
||||||
|
try {
|
||||||
|
if (configExists()) {
|
||||||
|
const cfg = loadConfig();
|
||||||
|
existing = cfg?.db || {};
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
existing = {};
|
||||||
|
}
|
||||||
|
|
||||||
return res.render("setup/index", {
|
return res.render("setup/index", {
|
||||||
title: "Erstinstallation",
|
title: configExists() ? "DB-Verbindung ändern" : "Erstinstallation",
|
||||||
|
isUpdate: configExists(),
|
||||||
defaults: {
|
defaults: {
|
||||||
host: "127.0.0.1",
|
host: existing.host || "85.215.63.122",
|
||||||
port: 3306,
|
port: existing.port || 3306,
|
||||||
user: "",
|
user: existing.user || "",
|
||||||
password: "",
|
password: "", // Passwort aus Sicherheitsgründen nie vorausfüllen
|
||||||
name: "",
|
name: existing.name || "",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Passwort auflösen: wenn leer → altes Passwort aus config.enc nehmen
|
||||||
|
*/
|
||||||
|
function resolvePassword(inputPassword) {
|
||||||
|
if (inputPassword && inputPassword.trim() !== "") {
|
||||||
|
return inputPassword;
|
||||||
|
}
|
||||||
|
// Passwort-Feld leer → altes Passwort aus bestehender Config beibehalten
|
||||||
|
try {
|
||||||
|
if (configExists()) {
|
||||||
|
const old = loadConfig();
|
||||||
|
return old?.db?.password || "";
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
/* ignore */
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ✅ Verbindung testen (AJAX)
|
* ✅ Verbindung testen (AJAX)
|
||||||
*/
|
*/
|
||||||
router.post("/test", blockIfInstalled, async (req, res) => {
|
router.post("/test", blockIfInstalled, async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { host, port, user, password, name } = req.body;
|
const { host, port, user, name } = req.body;
|
||||||
|
const password = resolvePassword(req.body.password);
|
||||||
|
|
||||||
if (!host || !user || !name) {
|
if (!host || !user || !name) {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
@ -51,12 +82,12 @@ router.post("/test", blockIfInstalled, async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const connection = await mysql.createConnection({
|
const connection = await mysql.createConnection({
|
||||||
host,
|
host: host.trim(),
|
||||||
port: Number(port || 3306),
|
port: Number(port || 3306),
|
||||||
user,
|
user: user.trim(),
|
||||||
password,
|
password,
|
||||||
database: name,
|
database: name.trim(),
|
||||||
connectTimeout: 5000,
|
connectTimeout: 6000,
|
||||||
});
|
});
|
||||||
|
|
||||||
await connection.query("SELECT 1");
|
await connection.query("SELECT 1");
|
||||||
@ -76,7 +107,9 @@ router.post("/test", blockIfInstalled, async (req, res) => {
|
|||||||
*/
|
*/
|
||||||
router.post("/", blockIfInstalled, async (req, res) => {
|
router.post("/", blockIfInstalled, async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { host, port, user, password, name } = req.body;
|
const { host, port, user, name } = req.body;
|
||||||
|
// Passwort: leer = altes Passwort beibehalten
|
||||||
|
const password = resolvePassword(req.body.password);
|
||||||
|
|
||||||
if (!host || !user || !name) {
|
if (!host || !user || !name) {
|
||||||
req.session.flash = req.session.flash || [];
|
req.session.flash = req.session.flash || [];
|
||||||
@ -88,41 +121,50 @@ router.post("/", blockIfInstalled, async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ✅ Verbindung testen bevor speichern
|
// ✅ Verbindung testen bevor speichern
|
||||||
const connection = await mysql.createConnection({
|
let connection;
|
||||||
host,
|
try {
|
||||||
|
connection = await mysql.createConnection({
|
||||||
|
host: host.trim(),
|
||||||
port: Number(port || 3306),
|
port: Number(port || 3306),
|
||||||
user,
|
user: user.trim(),
|
||||||
password,
|
password,
|
||||||
database: name,
|
database: name.trim(),
|
||||||
connectTimeout: 5000,
|
connectTimeout: 6000,
|
||||||
});
|
});
|
||||||
|
|
||||||
await connection.query("SELECT 1");
|
await connection.query("SELECT 1");
|
||||||
await connection.end();
|
await connection.end();
|
||||||
|
} catch (connErr) {
|
||||||
|
req.session.flash = req.session.flash || [];
|
||||||
|
req.session.flash.push({
|
||||||
|
type: "danger",
|
||||||
|
message: "❌ DB-Verbindung fehlgeschlagen: " + connErr.message,
|
||||||
|
});
|
||||||
|
return res.redirect("/setup");
|
||||||
|
}
|
||||||
|
|
||||||
// ✅ speichern
|
// ✅ In config.enc speichern
|
||||||
saveConfig({
|
saveConfig({
|
||||||
db: {
|
db: {
|
||||||
host,
|
host: host.trim(),
|
||||||
port: Number(port || 3306),
|
port: Number(port || 3306),
|
||||||
user,
|
user: user.trim(),
|
||||||
password,
|
password,
|
||||||
name,
|
name: name.trim(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// ✅ DB Pool neu starten (damit neue config sofort aktiv ist)
|
// ✅ DB Pool neu initialisieren (neue Config sofort aktiv)
|
||||||
if (typeof db.resetPool === "function") {
|
if (typeof db.resetPool === "function") {
|
||||||
db.resetPool();
|
db.resetPool();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ Session Store neu starten
|
// ✅ Session Store neu initialisieren
|
||||||
resetSessionStore();
|
resetSessionStore();
|
||||||
|
|
||||||
req.session.flash = req.session.flash || [];
|
req.session.flash = req.session.flash || [];
|
||||||
req.session.flash.push({
|
req.session.flash.push({
|
||||||
type: "success",
|
type: "success",
|
||||||
message: "✅ Setup abgeschlossen. Du kannst dich jetzt einloggen.",
|
message: "✅ DB-Verbindung gespeichert. Du kannst dich jetzt einloggen.",
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.redirect("/login");
|
return res.redirect("/login");
|
||||||
|
|||||||
@ -6,105 +6,177 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
*, *::before, *::after { box-sizing: border-box; }
|
||||||
body {
|
body {
|
||||||
font-family: Arial, sans-serif;
|
font-family: Arial, sans-serif;
|
||||||
background: #f5f5f5;
|
background: #f0f2f5;
|
||||||
padding: 20px;
|
padding: 40px 20px;
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: center;
|
||||||
}
|
}
|
||||||
.card {
|
.card {
|
||||||
max-width: 560px;
|
width: 100%;
|
||||||
margin: 0 auto;
|
max-width: 580px;
|
||||||
background: white;
|
background: white;
|
||||||
padding: 20px;
|
padding: 32px;
|
||||||
border-radius: 12px;
|
border-radius: 14px;
|
||||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
|
box-shadow: 0 6px 24px rgba(0, 0, 0, 0.10);
|
||||||
|
}
|
||||||
|
.card h2 {
|
||||||
|
margin: 0 0 6px 0;
|
||||||
|
font-size: 22px;
|
||||||
|
color: #111827;
|
||||||
|
}
|
||||||
|
.card p {
|
||||||
|
margin: 0 0 24px 0;
|
||||||
|
color: #6b7280;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.badge-update {
|
||||||
|
display: inline-block;
|
||||||
|
background: #fef3c7;
|
||||||
|
color: #92400e;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
padding: 3px 10px;
|
||||||
|
border-radius: 20px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
border: 1px solid #fcd34d;
|
||||||
}
|
}
|
||||||
label {
|
label {
|
||||||
display: block;
|
display: block;
|
||||||
margin-top: 12px;
|
margin-top: 16px;
|
||||||
|
margin-bottom: 5px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #374151;
|
||||||
}
|
}
|
||||||
input {
|
input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 10px;
|
padding: 10px 12px;
|
||||||
margin-top: 6px;
|
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
border: 1px solid #ddd;
|
border: 1px solid #d1d5db;
|
||||||
|
font-size: 14px;
|
||||||
|
outline: none;
|
||||||
|
transition: border-color 0.2s;
|
||||||
|
}
|
||||||
|
input:focus {
|
||||||
|
border-color: #2563eb;
|
||||||
|
box-shadow: 0 0 0 3px rgba(37,99,235,0.12);
|
||||||
}
|
}
|
||||||
.row {
|
.row {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
}
|
}
|
||||||
.row > div {
|
.row > div { flex: 1; }
|
||||||
flex: 1;
|
.btn-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
margin-top: 24px;
|
||||||
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
button {
|
button {
|
||||||
margin-top: 16px;
|
padding: 10px 18px;
|
||||||
padding: 10px 14px;
|
|
||||||
border: 0;
|
border: 0;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
transition: opacity 0.2s;
|
||||||
}
|
}
|
||||||
.btn-primary {
|
button:hover { opacity: 0.88; }
|
||||||
background: #2563eb;
|
.btn-primary { background: #2563eb; color: white; flex: 1; }
|
||||||
color: white;
|
.btn-secondary { background: #111827; color: white; }
|
||||||
}
|
|
||||||
.btn-secondary {
|
|
||||||
background: #111827;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
.msg {
|
.msg {
|
||||||
margin-top: 10px;
|
margin-top: 14px;
|
||||||
padding: 10px;
|
padding: 12px 14px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
|
font-size: 14px;
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
.msg.ok {
|
.msg.ok { background: #dcfce7; color: #166534; border: 1px solid #86efac; }
|
||||||
background: #dcfce7;
|
.msg.bad { background: #fee2e2; color: #991b1b; border: 1px solid #fca5a5; }
|
||||||
color: #166534;
|
.msg.pending { background: #eff6ff; color: #1d4ed8; border: 1px solid #93c5fd; }
|
||||||
}
|
.divider {
|
||||||
.msg.bad {
|
border: none;
|
||||||
background: #fee2e2;
|
border-top: 1px solid #e5e7eb;
|
||||||
color: #991b1b;
|
margin: 24px 0;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<h2>🛠️ Erstinstallation</h2>
|
<h2>🛠️ <%= title %></h2>
|
||||||
<p>
|
|
||||||
Bitte DB Daten eingeben. Danach wird
|
|
||||||
<code>config.enc</code> gespeichert.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<form method="POST" action="/setup">
|
<% if (typeof isUpdate !== 'undefined' && isUpdate) { %>
|
||||||
<label>DB Host</label>
|
<span class="badge-update">⚠️ Bestehende Konfiguration wird überschrieben</span>
|
||||||
<input name="host" placeholder="85.215.63.122" required />
|
<% } %>
|
||||||
|
|
||||||
<label>DB Port</label>
|
<p>DB-Verbindungsdaten eingeben. Die Verbindung wird vor dem Speichern geprüft.</p>
|
||||||
<input name="port" placeholder="3306" value="3306" required />
|
|
||||||
|
|
||||||
<label>DB Benutzer</label>
|
<form method="POST" action="/setup" id="setupForm">
|
||||||
<input name="user" placeholder="praxisuser" required />
|
|
||||||
|
|
||||||
<label>DB Passwort</label>
|
<div class="row">
|
||||||
<input name="password" type="password" required />
|
<div>
|
||||||
|
<label for="host">DB Host</label>
|
||||||
<label>DB Name</label>
|
|
||||||
<input name="name" placeholder="praxissoftware" required />
|
|
||||||
<label>Passwort</label>
|
|
||||||
<input
|
<input
|
||||||
name="password"
|
id="host"
|
||||||
type="password"
|
name="host"
|
||||||
value="<%= defaults.password %>"
|
placeholder="85.215.63.122"
|
||||||
|
value="<%= defaults.host %>"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div style="max-width:110px">
|
||||||
|
<label for="port">Port</label>
|
||||||
|
<input
|
||||||
|
id="port"
|
||||||
|
name="port"
|
||||||
|
placeholder="3306"
|
||||||
|
value="<%= defaults.port %>"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label for="user">DB Benutzer</label>
|
||||||
|
<input
|
||||||
|
id="user"
|
||||||
|
name="user"
|
||||||
|
placeholder="praxisuser"
|
||||||
|
value="<%= defaults.user %>"
|
||||||
|
required
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<label for="password">DB Passwort</label>
|
||||||
|
<input
|
||||||
|
id="password"
|
||||||
|
name="password"
|
||||||
|
type="password"
|
||||||
|
placeholder="<%= typeof isUpdate !== 'undefined' && isUpdate ? '(unverändert lassen = leer lassen)' : '' %>"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<label for="name">DB Name</label>
|
||||||
|
<input
|
||||||
|
id="name"
|
||||||
|
name="name"
|
||||||
|
placeholder="praxissoftware"
|
||||||
|
value="<%= defaults.name %>"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
|
||||||
|
<hr class="divider" />
|
||||||
|
|
||||||
|
<div class="btn-row">
|
||||||
<button type="button" class="btn-secondary" onclick="testConnection()">
|
<button type="button" class="btn-secondary" onclick="testConnection()">
|
||||||
🔍 Verbindung testen
|
🔍 Verbindung testen
|
||||||
</button>
|
</button>
|
||||||
<button type="submit" class="btn-primary">
|
<button type="submit" class="btn-primary">
|
||||||
✅ Speichern & Setup abschließen
|
✅ Speichern & abschließen
|
||||||
</button>
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="msg" class="msg"></div>
|
<div id="msg" class="msg"></div>
|
||||||
</form>
|
</form>
|
||||||
@ -112,14 +184,19 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
async function testConnection() {
|
async function testConnection() {
|
||||||
const form = document.querySelector("form");
|
|
||||||
const data = new FormData(form);
|
|
||||||
const body = Object.fromEntries(data.entries());
|
|
||||||
|
|
||||||
const msg = document.getElementById("msg");
|
const msg = document.getElementById("msg");
|
||||||
|
msg.className = "msg pending";
|
||||||
msg.style.display = "block";
|
msg.style.display = "block";
|
||||||
msg.className = "msg";
|
msg.textContent = "⏳ Teste Verbindung...";
|
||||||
msg.textContent = "Teste Verbindung...";
|
|
||||||
|
// Felder einzeln auslesen (sicherer als FormData bei doppelten Namen)
|
||||||
|
const body = {
|
||||||
|
host: document.getElementById("host").value.trim(),
|
||||||
|
port: document.getElementById("port").value.trim(),
|
||||||
|
user: document.getElementById("user").value.trim(),
|
||||||
|
password: document.getElementById("password").value,
|
||||||
|
name: document.getElementById("name").value.trim(),
|
||||||
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await fetch("/setup/test", {
|
const res = await fetch("/setup/test", {
|
||||||
@ -129,13 +206,11 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
const json = await res.json();
|
const json = await res.json();
|
||||||
|
msg.className = "msg " + (json.ok ? "ok" : "bad");
|
||||||
msg.textContent = json.message;
|
msg.textContent = json.message;
|
||||||
|
|
||||||
if (json.ok) msg.classList.add("ok");
|
|
||||||
else msg.classList.add("bad");
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
msg.textContent = "❌ Fehler: " + e.message;
|
msg.className = "msg bad";
|
||||||
msg.classList.add("bad");
|
msg.textContent = "❌ Netzwerkfehler: " + e.message;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user