Finalizing user_creation
This commit is contained in:
parent
b260255980
commit
08c7ef4660
|
|
@ -9,6 +9,14 @@ TAG="latest"
|
|||
|
||||
echo "🚀 Starting deployment for Haumdaucher..."
|
||||
|
||||
# Check cluster connectivity
|
||||
echo "🔌 Verifying Kubernetes cluster connectivity..."
|
||||
if ! kubectl cluster-info > /dev/null 2>&1; then
|
||||
echo "❌ Error: Kubernetes cluster is unreachable (kubectl failed). Please check your KUBECONFIG or VPN."
|
||||
exit 1
|
||||
fi
|
||||
echo "✅ Cluster is reachable."
|
||||
|
||||
# Create namespace if it doesn't exist
|
||||
kubectl create namespace $NAMESPACE --dry-run=client -o yaml | kubectl apply -f -
|
||||
|
||||
|
|
|
|||
24
src/App.vue
24
src/App.vue
|
|
@ -8,6 +8,7 @@ import Datenschutz from './components/sections/Legal/Datenschutz.vue'
|
|||
import HaumdaucherGame from './components/layout/HaumdaucherGame.vue'
|
||||
import { messages } from './locales/i18n'
|
||||
import { useAuth } from './composables/useAuth'
|
||||
import PasswordReset from './components/auth/PasswordReset.vue'
|
||||
|
||||
const { isAllowed } = useAuth()
|
||||
|
||||
|
|
@ -15,6 +16,8 @@ const theme = ref('classic')
|
|||
const showGame = ref(false)
|
||||
const isNatUnlocked = ref(false)
|
||||
const boars = ref<{id: number, top: number}[]>([])
|
||||
const isPasswordResetMode = ref(false)
|
||||
const resetOobCode = ref('')
|
||||
|
||||
const toggleTheme = (newTheme: string) => {
|
||||
theme.value = newTheme
|
||||
|
|
@ -52,6 +55,12 @@ const startBoarRun = () => {
|
|||
}
|
||||
|
||||
onMounted(() => {
|
||||
const urlParams = new URLSearchParams(window.location.search)
|
||||
if (urlParams.get('mode') === 'resetPassword') {
|
||||
isPasswordResetMode.value = true
|
||||
resetOobCode.value = urlParams.get('oobCode') || ''
|
||||
}
|
||||
|
||||
const savedTheme = localStorage.getItem('haumdaucher-theme')
|
||||
if (savedTheme) toggleTheme(savedTheme)
|
||||
|
||||
|
|
@ -72,9 +81,17 @@ const t = (key: string) => {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<Header
|
||||
:currentTheme="theme"
|
||||
:isNatUnlocked="isNatUnlocked"
|
||||
<div v-if="isPasswordResetMode">
|
||||
<PasswordReset
|
||||
:oobCode="resetOobCode"
|
||||
:t="t"
|
||||
@close="isPasswordResetMode = false; window.history.replaceState({}, document.title, window.location.pathname)"
|
||||
/>
|
||||
</div>
|
||||
<div v-else>
|
||||
<Header
|
||||
:currentTheme="theme"
|
||||
:isNatUnlocked="isNatUnlocked"
|
||||
@update:theme="toggleTheme"
|
||||
@open:game="showGame = true"
|
||||
:t="t"
|
||||
|
|
@ -117,6 +134,7 @@ const t = (key: string) => {
|
|||
</nav>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,202 @@
|
|||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { confirmPasswordReset, verifyPasswordResetCode } from 'firebase/auth'
|
||||
import { auth } from '../../firebase'
|
||||
|
||||
const props = defineProps<{
|
||||
oobCode: string
|
||||
t: (key: string) => any
|
||||
}>()
|
||||
|
||||
const emit = defineEmits(['close'])
|
||||
|
||||
const newPassword = ref('')
|
||||
const confirmPassword = ref('')
|
||||
const error = ref('')
|
||||
const success = ref(false)
|
||||
const isLoading = ref(false)
|
||||
|
||||
const handleReset = async () => {
|
||||
error.value = ''
|
||||
|
||||
if (newPassword.value !== confirmPassword.value) {
|
||||
error.value = 'Die Passwörter stimmen nicht überein.'
|
||||
return
|
||||
}
|
||||
|
||||
if (newPassword.value.length < 6) {
|
||||
error.value = 'Das Passwort muss mindestens 6 Zeichen lang sein.'
|
||||
return
|
||||
}
|
||||
|
||||
isLoading.value = true
|
||||
|
||||
try {
|
||||
// Optional: First verify the code is valid
|
||||
const email = await verifyPasswordResetCode(auth, props.oobCode)
|
||||
|
||||
// Then confirm the reset
|
||||
await confirmPasswordReset(auth, props.oobCode, newPassword.value)
|
||||
success.value = true
|
||||
} catch (err: any) {
|
||||
console.error('Password reset error:', err)
|
||||
if (err.code === 'auth/invalid-action-code') {
|
||||
error.value = 'Der Link ist ungültig oder abgelaufen. Bitte fordere einen neuen an.'
|
||||
} else {
|
||||
error.value = 'Ein Fehler ist aufgetreten: ' + err.message
|
||||
}
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="reset-container">
|
||||
<div class="glass-panel auth-card">
|
||||
<h2 class="section-title">Neues Passwort 🌭</h2>
|
||||
|
||||
<div v-if="success" class="success-message">
|
||||
<h3>Sauber!</h3>
|
||||
<p>Dein Passwort wurde erfolgreich geändert.</p>
|
||||
<button class="club-btn" @click="emit('close')">Zurück zur Startseite</button>
|
||||
</div>
|
||||
|
||||
<form v-else @submit.prevent="handleReset" class="auth-form">
|
||||
<p class="description">Bitte gib dein neues geheimes Haumdaucher-Passwort ein.</p>
|
||||
|
||||
<div class="input-group">
|
||||
<label>Neues Passwort</label>
|
||||
<input
|
||||
type="password"
|
||||
v-model="newPassword"
|
||||
placeholder="Mindestens 6 Zeichen"
|
||||
required
|
||||
class="club-input"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label>Passwort bestätigen</label>
|
||||
<input
|
||||
type="password"
|
||||
v-model="confirmPassword"
|
||||
placeholder="Nochmal zur Sicherheit"
|
||||
required
|
||||
class="club-input"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-if="error" class="error-alert">{{ error }}</div>
|
||||
|
||||
<div class="actions">
|
||||
<button type="button" class="club-btn text-btn" @click="emit('close')">Abbrechen</button>
|
||||
<button type="submit" class="club-btn" :disabled="isLoading">
|
||||
{{ isLoading ? 'Speichern...' : 'Passwort ändern' }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.reset-container {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 20px;
|
||||
background: var(--bg-color);
|
||||
}
|
||||
|
||||
.auth-card {
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
padding: 40px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.description {
|
||||
margin-bottom: 30px;
|
||||
opacity: 0.8;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.auth-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.input-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.input-group label {
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.club-input {
|
||||
padding: 12px 15px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--glass-border);
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
color: var(--text-color);
|
||||
font-size: 1rem;
|
||||
font-family: inherit;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.club-input:focus {
|
||||
outline: none;
|
||||
border-color: var(--primary-color);
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.actions .club-btn {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.text-btn {
|
||||
background: transparent !important;
|
||||
border: 1px solid var(--glass-border) !important;
|
||||
}
|
||||
|
||||
.text-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.05) !important;
|
||||
}
|
||||
|
||||
.error-alert {
|
||||
background: rgba(255, 75, 75, 0.1);
|
||||
color: #ff4b4b;
|
||||
padding: 10px;
|
||||
border-radius: 8px;
|
||||
font-size: 0.9rem;
|
||||
border: 1px solid rgba(255, 75, 75, 0.3);
|
||||
}
|
||||
|
||||
.success-message {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
align-items: center;
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.success-message h3 {
|
||||
color: #4caf50;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
# Firebase Email Templates (HAUMDAUCHER n.e.V.)
|
||||
|
||||
Da Google Cloud Identity Platform (Firebase) die Konfiguration von E-Mail-Vorlagen über Terraform (`google_identity_platform_config`) nicht nativ unterstützt, müssen diese Texte einmalig manuell in der Firebase Console hinterlegt werden.
|
||||
|
||||
## Globale Einstellungen für alle Templates
|
||||
Bitte stelle in der Firebase Console unter **Authentication > Templates** (oder Identity Platform im GCP Dashboard) Folgendes ein:
|
||||
- **Sender Name**: HAUMDAUCHER n.e.V.
|
||||
- **Reply-to**: info@haumdaucher.de
|
||||
|
||||
*(Da `haumdaucher.de` bereits bei Google verifiziert ist, sollte der Versand über diese Domain problemlos funktionieren).*
|
||||
|
||||
---
|
||||
|
||||
## 1. Passwort zurücksetzen (Password Reset)
|
||||
|
||||
**Subject:**
|
||||
```text
|
||||
🌭 Neues Passwort gefällig? (HAUMDAUCHER n.e.V.)
|
||||
```
|
||||
|
||||
**Message (Above Link):**
|
||||
```text
|
||||
Servus %DISPLAY_NAME%,
|
||||
|
||||
ohje, hast du dein Passwort für den Haumdaucher-Zugang verschwitzt? Keine Sorge, das passiert den Besten nach ein paar Bratwürsten und Knackern zu viel! 🍻
|
||||
|
||||
Damit du wieder vollen Zugriff auf unsere streng geheimen Wurst- und Spezialitäten-Archive in Regensburg bekommst, klick einfach auf den magischen Link unten:
|
||||
```
|
||||
|
||||
**Message (Below Link):**
|
||||
```text
|
||||
Lass dir Zeit, der Link rennt nicht weg (anders als eine gute Wurst auf dem Grill 🔥). Falls du gar kein neues Passwort angefordert hast, ignoriere diese E-Mail einfach – dein Account ist sicher.
|
||||
|
||||
Mit fleischigen Grüßen,
|
||||
Dein Vorstand des HAUMDAUCHER Wurst und Spezialitäten n.e.V.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. E-Mail-Adresse bestätigen (Email Address Verification)
|
||||
|
||||
**Subject:**
|
||||
```text
|
||||
🥓 Bestätige deine E-Mail für den Haumdaucher-Zirkel!
|
||||
```
|
||||
|
||||
**Message (Above Link):**
|
||||
```text
|
||||
Servus %DISPLAY_NAME%,
|
||||
|
||||
willkommen bei den wahren Feinschmeckern! 🥩 Bevor wir dich in die tiefsten Geheimnisse unserer Wurst- und Spezialitätenkunde einweihen, müssen wir kurz checken, ob du auch ein echter Mensch bist (und kein veganer Spionage-Bot).
|
||||
|
||||
Bitte bestätige deine E-Mail-Adresse mit einem herzhaften Klick auf diesen Link:
|
||||
```
|
||||
|
||||
**Message (Below Link):**
|
||||
```text
|
||||
Sobald das erledigt ist, bist du offiziell startklar. Wir freuen uns auf dich! 🍻
|
||||
|
||||
Mit fleischigen Grüßen,
|
||||
Dein Vorstand des HAUMDAUCHER Wurst und Spezialitäten n.e.V.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. E-Mail-Adresse ändern (Email Address Change)
|
||||
|
||||
**Subject:**
|
||||
```text
|
||||
🚨 E-Mail-Änderung beim Haumdaucher n.e.V.!
|
||||
```
|
||||
|
||||
**Message (Above Link):**
|
||||
```text
|
||||
Servus %DISPLAY_NAME%,
|
||||
|
||||
wir haben gehört, dass du eine neue E-Mail-Adresse für deinen Haumdaucher-Account verwenden möchtest. Hast du den Provider gewechselt oder war die alte Adresse zu vegetarisch? 🥦
|
||||
|
||||
Bitte bestätige deine neue Adresse hier:
|
||||
```
|
||||
|
||||
**Message (Below Link):**
|
||||
```text
|
||||
Falls du diese Änderung NICHT angefordert hast, melde dich bitte sofort bei uns unter info@haumdaucher.de – vielleicht versucht jemand, an deine geheimen Regensburger Wurst-Rezepte zu kommen! 🕵️♂️
|
||||
|
||||
Mit fleischigen Grüßen,
|
||||
Dein Vorstand des HAUMDAUCHER Wurst und Spezialitäten n.e.V.
|
||||
```
|
||||
|
|
@ -2,7 +2,7 @@ const CONFIG = {
|
|||
SPREADSHEET_ID: "1q4r08nBA_ClWv3ypPCQ6GVCfMVkQwSKzDSRiokkQQ8Q", // ID from mail_forwarding
|
||||
SHEET_NAME: "Form Responses 1",
|
||||
COL_FORWARD_TO_ADDRESS: 4, // 1-indexed (Column D from mail_forwarding)
|
||||
ADMIN_EMAIL: "moritz@haumdaucher.de",
|
||||
ADMIN_EMAIL: "info@haumdaucher.de",
|
||||
PROJECT_ID: "haumdaucher", // Used in the Identity Toolkit REST API
|
||||
DRY_RUN: false,
|
||||
SEND_EMAIL_ON_CREATION: false,
|
||||
|
|
|
|||
Loading…
Reference in New Issue