Files
Aberwyn/Aberwyn/Views/Setup/Index.cshtml
Elias Jansson 908bc469c6
All checks were successful
continuous-integration/drone/push Build is passing
Setup!
2025-06-03 21:36:16 +02:00

396 lines
15 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
@model Aberwyn.Models.SetupSettings
@{
ViewData["Title"] = "Installera Aberwyn";
}
<h1>Installera Aberwyn</h1>
@if (ViewBag.Error != null)
{
<div class="alert alert-danger">@ViewBag.Error</div>
}
<style>
.setup-step {
margin-bottom: 2rem;
padding: 1.5rem;
background-color: #f8f9fa;
border-radius: 8px;
box-shadow: 0 2px 6px rgba(0,0,0,0.05);
}
.form-group {
margin-bottom: 1rem;
display: flex;
flex-direction: column;
}
label {
font-weight: bold;
margin-bottom: 0.2rem;
}
input.form-control {
padding: 0.5rem;
font-size: 1rem;
border-radius: 6px;
}
.is-invalid {
border-color: #dc3545;
}
.invalid-feedback {
color: #dc3545;
font-size: 0.875rem;
display: none;
}
.is-invalid + .invalid-feedback {
display: block;
}
button {
padding: 10px 20px;
font-size: 1rem;
border-radius: 6px;
border: none;
cursor: pointer;
transition: background 0.2s ease;
margin-top: 0.5rem;
margin-right: 0.5rem;
}
.btn-primary { background-color: #6a0dad; color: white; }
.btn-primary:hover { background-color: #5a0cab; }
.btn-secondary { background-color: #adb5bd; color: black; }
.btn-secondary:hover { background-color: #9ca3af; }
.btn-success { background-color: #198754; color: white; }
.btn-success:hover { background-color: #157347; }
.btn-danger { background-color: #dc3545; color: white; }
.btn-danger:hover { background-color: #bb2d3b; }
.btn-outline-info {
border: 1px solid #0dcaf0;
color: #0dcaf0;
background: none;
}
.btn-outline-info:hover {
background-color: #0dcaf0;
color: white;
}
#setup-summary ul {
list-style: none;
padding-left: 0;
}
#setup-summary li {
margin-bottom: 0.4rem;
}
.spinner {
display: inline-block;
width: 1.2rem;
height: 1.2rem;
border: 3px solid rgba(0,0,0,0.2);
border-top-color: #6a0dad;
border-radius: 50%;
animation: spin 1s linear infinite;
margin-left: 0.5rem;
}
@@keyframes spin {
to { transform: rotate(360deg); }
}
</style>
<form method="post" id="setup-form">
<div id="step-1" class="setup-step">
<h2>Steg 1: Databasinställningar</h2>
<div class="form-group">
<label for="DbHost">Server</label>
<input asp-for="DbHost" name="DbHost" class="form-control" id="DbHost" required />
<div class="invalid-feedback">Fältet är obligatoriskt</div>
</div>
<div class="form-group">
<label for="DbPort">Port</label>
<input asp-for="DbPort" name="DbPort" class="form-control" id="DbPort" value="3306" required />
<div class="invalid-feedback">Fältet är obligatoriskt</div>
</div>
<div class="form-group">
<label for="DbName">Databasnamn</label>
<input asp-for="DbName" name="DbName" class="form-control" id="DbName" required />
<div class="invalid-feedback">Fältet är obligatoriskt</div>
</div>
<div class="form-group">
<label for="DbUser">Användarnamn</label>
<input asp-for="DbUser" name="DbUser" class="form-control" id="DbUser" required />
<div class="invalid-feedback">Fältet är obligatoriskt</div>
</div>
<div class="form-group">
<label for="DbPassword">Lösenord</label>
<input asp-for="DbPassword" name="DbPassword" type="password" class="form-control" id="DbPassword" required />
<div class="invalid-feedback">Fältet är obligatoriskt</div>
</div>
<button type="button" class="btn btn-outline-info" onclick="testDbConnection()">Testa anslutning</button>
<div id="db-test-result" class="mt-2"></div>
<button type="button" class="btn btn-primary" onclick="validateStep(1) && goToStep(2)">Nästa</button>
</div>
<div id="step-2" class="setup-step" style="display: none;">
<h2>Steg 2: Administratör</h2>
<div class="form-group">
<label for="AdminUsername">Admin Användarnamn</label>
<input asp-for="AdminUsername" name="AdminUsername" class="form-control" id="AdminUsername" required />
<div class="invalid-feedback">Fältet är obligatoriskt</div>
</div>
<div class="form-group">
<label for="AdminEmail">E-postadress</label>
<input asp-for="AdminEmail" name="AdminEmail" type="email" class="form-control" id="AdminEmail" required />
<div class="invalid-feedback">Fältet är obligatoriskt</div>
</div>
<div class="form-group">
<label for="AdminPassword">Lösenord</label>
<input asp-for="AdminPassword" name="AdminPassword" type="password" class="form-control"
id="AdminPassword"
pattern="(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^a-zA-Z\d]).{6,}"
title="Minst 6 tecken, inklusive versal, gemen, siffra och specialtecken"
required />
<div class="invalid-feedback">Ange ett giltigt lösenord enligt kraven</div>
</div>
<div class="form-group">
<label for="ConfirmPassword">Bekräfta lösenord</label>
<input type="password" id="ConfirmPassword" class="form-control" required />
<span id="password-match-status" style="margin-top: 4px; font-size: 1.2rem;"></span>
<div class="invalid-feedback" id="confirmPasswordError">Lösenorden matchar inte</div>
</div>
<ul id="password-criteria" style="list-style: none; padding-left: 0; font-size: 0.9rem; margin-top: 0.5rem;">
<li id="length-criteria">❌ Minst 6 tecken</li>
<li id="uppercase-criteria">❌ Minst en stor bokstav (AZ)</li>
<li id="lowercase-criteria">❌ Minst en liten bokstav (az)</li>
<li id="digit-criteria">❌ Minst en siffra (09)</li>
<li id="special-criteria">❌ Minst ett specialtecken (!#&...)</li>
</ul>
<button type="button" class="btn btn-secondary" onclick="goToStep(1)">Tillbaka</button>
<button type="button" class="btn btn-primary" onclick="validateAdminStep() && goToStep(3)">Nästa</button>
</div>
<div id="step-3" class="setup-step" style="display: none;">
<h2>Steg 3: Konfiguration pågår...</h2>
<div id="setup-summary">
<ul>
<li><strong>Server:</strong> <span id="summary-DbHost"></span></li>
<li><strong>Port:</strong> <span id="summary-DbPort"></span></li>
<li><strong>Databas:</strong> <span id="summary-DbName"></span></li>
<li><strong>Användare:</strong> <span id="summary-DbUser"></span></li>
<li><strong>Adminnamn:</strong> <span id="summary-AdminUsername"></span></li>
<li><strong>Admin-e-post:</strong> <span id="summary-AdminEmail"></span></li>
</ul>
</div>
<div id="setup-progress" class="mt-3">
<p>
Skapar databastabeller och konfigurerar administratörskonto...
<span class="spinner"></span>
</p>
</div>
<button type="button" class="btn btn-secondary" onclick="goToStep(2)">Tillbaka</button>
<button type="button" class="btn btn-success" onclick="submitSetup()">Slutför installation</button>
</div>
</form>
@if (User.IsInRole("Admin"))
{
<form method="post" asp-action="Reset" class="mt-3" onsubmit="return confirm('Är du säker på att du vill återställa konfigurationen?')">
<button type="submit" class="btn btn-danger">Återställ inställningar</button>
</form>
}
@section Scripts {
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.5/jquery.validate.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate-unobtrusive/3.2.12/jquery.validate.unobtrusive.min.js"></script>
<script>
function goToStep(step) {
document.querySelectorAll('.setup-step').forEach(s => s.style.display = 'none');
document.getElementById('step-' + step).style.display = 'block';
if (step === 3) {
document.getElementById('summary-DbHost').textContent = document.getElementById('DbHost').value;
document.getElementById('summary-DbPort').textContent = document.getElementById('DbPort').value;
document.getElementById('summary-DbName').textContent = document.getElementById('DbName').value;
document.getElementById('summary-DbUser').textContent = document.getElementById('DbUser').value;
document.getElementById('summary-AdminUsername').textContent = document.getElementById('AdminUsername').value;
document.getElementById('summary-AdminEmail').textContent = document.getElementById('AdminEmail').value;
document.getElementById('setup-progress').innerHTML = `<p>Redo att påbörja installationen.</p>`;
}
}
document.getElementById('AdminPassword').addEventListener('input', function () {
const val = this.value;
const hasLength = val.length >= 6;
const hasUpper = /[A-Z]/.test(val);
const hasLower = /[a-z]/.test(val);
const hasDigit = /\d/.test(val);
const hasSpecial = /[!@@#$%^&*(),.?":{}|<>_\-\\[\]\/+=~`]/.test(val); // lägg till specialtecken
updateCriteria('length-criteria', hasLength);
updateCriteria('uppercase-criteria', hasUpper);
updateCriteria('lowercase-criteria', hasLower);
updateCriteria('digit-criteria', hasDigit);
updateCriteria('special-criteria', hasSpecial);
});
function updateCriteria(id, isValid) {
const el = document.getElementById(id);
if (isValid) {
el.textContent = '✔ ' + el.textContent.slice(2);
el.style.color = 'green';
} else {
el.textContent = '❌ ' + el.textContent.slice(2);
el.style.color = 'red';
}
}
function validateStep(step) {
let isValid = true;
document.querySelectorAll('#step-' + step + ' [required]').forEach(field => {
if (!field.value.trim()) {
field.classList.add('is-invalid');
isValid = false;
} else {
field.classList.remove('is-invalid');
}
});
return isValid;
}
function testDbConnection() {
const host = document.getElementById('DbHost').value;
const port = document.getElementById('DbPort').value;
const db = document.getElementById('DbName').value;
const user = document.getElementById('DbUser').value;
const pass = document.getElementById('DbPassword').value;
const output = document.getElementById('db-test-result');
output.innerHTML = '<span class="spinner"></span> Testar...';
fetch('/api/setup/testdb', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ host, port, db, user, pass })
})
.then(async res => {
const text = await res.text();
console.log("Raw:", text);
try {
const result = JSON.parse(text);
if (result.success) {
output.innerHTML = '✅ <span class="text-success">Anslutning lyckades!</span>';
} else {
output.innerHTML = '❌ <span class="text-danger">' + result.message + '</span>';
}
} catch (e) {
output.innerHTML = '❌ <span class="text-danger">Felaktigt svar från servern: ' + text + '</span>';
}
})
.catch(err => {
output.innerHTML = '❌ <span class="text-danger">Nätverksfel: ' + err.message + '</span>';
});
}
function validateAdminStep() {
let valid = validateStep(2);
const pass = document.getElementById('AdminPassword');
const confirm = document.getElementById('ConfirmPassword');
const confirmError = document.getElementById('confirmPasswordError');
if (pass.value !== confirm.value) {
confirm.classList.add('is-invalid');
confirmError.style.display = 'block';
valid = false;
} else {
confirm.classList.remove('is-invalid');
confirmError.style.display = 'none';
}
return valid;
}
document.getElementById('ConfirmPassword').addEventListener('input', function () {
const pass = document.getElementById('AdminPassword').value;
const confirm = this.value;
const confirmField = document.getElementById('ConfirmPassword');
const matchStatus = document.getElementById('password-match-status');
if (confirm.length === 0) {
matchStatus.innerHTML = '';
confirmField.classList.remove('is-invalid');
return;
}
if (pass === confirm) {
matchStatus.innerHTML = '✔';
matchStatus.style.color = 'green';
confirmField.classList.remove('is-invalid');
} else {
matchStatus.innerHTML = '❌';
matchStatus.style.color = 'red';
confirmField.classList.add('is-invalid');
}
});
async function submitSetup() {
goToStep(3);
const form = document.getElementById('setup-form');
const formData = new FormData(form);
const data = {};
formData.forEach((value, key) => { data[key] = value; });
const resultEl = document.getElementById('setup-progress');
resultEl.innerHTML = `<p>
Skapar databastabeller och konfigurerar administratörskonto...
<span class="spinner"></span>
</p>`;
try {
const response = await fetch('/setup', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
const text = await response.text();
let result;
try {
result = JSON.parse(text);
} catch (err) {
throw new Error("Kunde inte tolka JSON-svar: " + text);
}
if (!response.ok) {
resultEl.innerHTML = `
<div class="alert alert-danger mt-3">
❌ Fel vid installation: ${result.error || 'Okänt fel'}
</div>`;
return;
}
resultEl.innerHTML = `
<div class="alert alert-success mt-3">
✅ ${result.message || 'Installation slutförd!'}
<br />
<button class="btn btn-success mt-3" onclick="window.location.href='/'">Gå vidare</button>
</div>`;
} catch (err) {
console.error("Något gick fel:", err);
resultEl.innerHTML = `<div class="alert alert-danger mt-3">
❌ Fel vid installation: ${err.message}
</div>`;
}
}
</script>
}