Refactor app architecture and clean local artifacts

This commit is contained in:
AzuTear
2026-06-24 23:43:14 +02:00
parent 17134b3b82
commit fef1d36fe8
274 changed files with 37724 additions and 6065 deletions
@@ -0,0 +1,220 @@
import { computed, onMounted, reactive, ref, watch } from 'vue'
import { ApiRequestError, api } from '../../lib/api'
import { clearSiteStatusCache } from '../../lib/siteStatus'
import type { AdminOperationalSettingsResponse } from '../../types/awards'
import type { AdminOperationalSettingsForm, AdminSettingsStatusSummary } from './adminSettingsTypes'
const fallbackMaintenanceTitle = 'Sternenpause'
const fallbackMaintenanceMessage = 'Die Award-Galaxie wird gerade liebevoll poliert. Schau gleich wieder vorbei.'
export function useAdminOperationalSettings() {
const operationalLoading = ref(true)
const operationalSaving = ref(false)
const operationalError = ref('')
const operationalSuccess = ref('')
const demoPasswordSet = ref(false)
const demoManagedByDatabase = ref(false)
const demoPasswordInput = ref('')
const savedOperationalSnapshot = ref('')
const operationalForm = reactive<AdminOperationalSettingsForm>({
demoLoginEnabled: false,
demoLoginIdentifier: '',
demoLoginTwitchUserId: '',
demoLoginDisplayName: '',
maintenanceModeEnabled: false,
maintenanceTitle: fallbackMaintenanceTitle,
maintenanceMessage: fallbackMaintenanceMessage,
})
const demoPasswordHint = computed(() => {
if (demoPasswordInput.value.trim()) {
return 'Dieses neue Passwort wird beim Speichern gesetzt.'
}
if (demoPasswordSet.value) {
return demoManagedByDatabase.value
? 'Passwort ist gesetzt. Leer lassen, wenn es bleiben soll.'
: 'Aktuell kommt das Passwort aus der Demo-Config. Beim Speichern wird es in die DB übernommen.'
}
return 'Noch kein Demo-Passwort gesetzt. Zum Aktivieren bitte ein Passwort vergeben.'
})
const demoCredentialsComplete = computed(() =>
!operationalForm.demoLoginEnabled
|| (
Boolean(operationalForm.demoLoginIdentifier.trim())
&& Boolean(operationalForm.demoLoginTwitchUserId.trim())
&& Boolean(operationalForm.demoLoginDisplayName.trim())
&& (demoPasswordSet.value || Boolean(demoPasswordInput.value.trim()))
),
)
const operationalSummary = computed<AdminSettingsStatusSummary[]>(() => [
{
label: 'Demo Login',
value: operationalForm.demoLoginEnabled ? 'Aktiv' : 'Aus',
note: operationalForm.demoLoginEnabled
? demoCredentialsComplete.value
? 'Öffentlicher Zugriff ist geschützt.'
: 'Aktivierung braucht vollständige Zugangsdaten.'
: 'Landingpage ist öffentlich erreichbar.',
tone: operationalForm.demoLoginEnabled
? demoCredentialsComplete.value ? 'warning' : 'danger'
: 'good',
},
{
label: 'Wartungsmodus',
value: operationalForm.maintenanceModeEnabled ? 'Aktiv' : 'Aus',
note: operationalForm.maintenanceModeEnabled
? 'Besucher sehen die Wartungsseite.'
: 'Öffentliche Seiten werden normal ausgeliefert.',
tone: operationalForm.maintenanceModeEnabled ? 'warning' : 'good',
},
{
label: 'Passwort',
value: demoPasswordSet.value ? 'Gesetzt' : 'Fehlt',
note: demoManagedByDatabase.value ? 'Quelle: Datenbank' : 'Quelle: App-Konfiguration',
tone: demoPasswordSet.value ? 'good' : 'warning',
},
])
const hasUnsavedOperationalChanges = computed(() =>
Boolean(savedOperationalSnapshot.value) &&
buildOperationalSnapshot() !== savedOperationalSnapshot.value,
)
function applyOperationalSettings(response: AdminOperationalSettingsResponse) {
operationalForm.demoLoginEnabled = response.demoLoginEnabled
operationalForm.demoLoginIdentifier = response.demoLoginEmail
operationalForm.demoLoginTwitchUserId = response.demoLoginTwitchUserId
operationalForm.demoLoginDisplayName = response.demoLoginDisplayName
operationalForm.maintenanceModeEnabled = response.maintenanceModeEnabled
operationalForm.maintenanceTitle = response.maintenanceTitle
operationalForm.maintenanceMessage = response.maintenanceMessage
demoPasswordSet.value = response.demoLoginPasswordSet
demoManagedByDatabase.value = response.demoLoginManagedByDatabase
demoPasswordInput.value = ''
rememberSavedOperationalSettings()
}
function buildOperationalSnapshot() {
return JSON.stringify({
demoLoginEnabled: operationalForm.demoLoginEnabled,
demoLoginIdentifier: operationalForm.demoLoginIdentifier,
demoLoginTwitchUserId: operationalForm.demoLoginTwitchUserId,
demoLoginDisplayName: operationalForm.demoLoginDisplayName,
maintenanceModeEnabled: operationalForm.maintenanceModeEnabled,
maintenanceTitle: operationalForm.maintenanceTitle,
maintenanceMessage: operationalForm.maintenanceMessage,
demoPasswordInput: demoPasswordInput.value,
})
}
function rememberSavedOperationalSettings() {
savedOperationalSnapshot.value = buildOperationalSnapshot()
}
async function loadOperationalSettings(options: { silent?: boolean } = {}) {
if (!options.silent) {
operationalLoading.value = true
operationalError.value = ''
}
try {
applyOperationalSettings(await api.getAdminOperationalSettings())
} catch (error) {
operationalError.value = error instanceof ApiRequestError
? error.message
: 'Operational Settings konnten nicht geladen werden.'
} finally {
if (!options.silent) {
operationalLoading.value = false
}
}
}
function validateOperationalSettings() {
if (demoPasswordInput.value.trim() && demoPasswordInput.value.trim().length < 12) {
operationalError.value = 'Das Demo-Passwort muss mindestens 12 Zeichen lang sein.'
return false
}
if (operationalForm.demoLoginEnabled && !demoCredentialsComplete.value) {
operationalError.value = 'Demo-Login braucht Login, Twitch-ID, Anzeigenamen und ein gesetztes Passwort.'
return false
}
return true
}
async function saveOperationalSettings() {
operationalError.value = ''
operationalSuccess.value = ''
if (!validateOperationalSettings()) {
return
}
operationalSaving.value = true
try {
const result = await api.updateAdminOperationalSettings({
...operationalForm,
demoLoginEmail: operationalForm.demoLoginIdentifier,
demoLoginPassword: demoPasswordInput.value.trim() || undefined,
})
demoPasswordSet.value = result.demoLoginPasswordSet
demoManagedByDatabase.value = true
demoPasswordInput.value = ''
await loadOperationalSettings({ silent: true })
clearSiteStatusCache()
operationalSuccess.value = 'Demo-Zugang und Wartungsmodus wurden gespeichert.'
} catch (error) {
operationalError.value = error instanceof ApiRequestError
? error.message
: 'Operational Settings konnten nicht gespeichert werden.'
} finally {
operationalSaving.value = false
}
}
watch(
() => [
operationalForm.demoLoginEnabled,
operationalForm.demoLoginIdentifier,
operationalForm.demoLoginTwitchUserId,
operationalForm.demoLoginDisplayName,
operationalForm.maintenanceModeEnabled,
operationalForm.maintenanceTitle,
operationalForm.maintenanceMessage,
demoPasswordInput.value,
],
() => {
if (operationalLoading.value || operationalSaving.value) return
operationalError.value = ''
operationalSuccess.value = ''
},
)
onMounted(loadOperationalSettings)
return {
operationalLoading,
operationalSaving,
operationalError,
operationalSuccess,
operationalForm,
demoPasswordSet,
demoManagedByDatabase,
demoPasswordInput,
demoPasswordHint,
demoCredentialsComplete,
operationalSummary,
hasUnsavedOperationalChanges,
loadOperationalSettings,
saveOperationalSettings,
}
}