feat: Registrierung default AN, Einladungslinks, System-Email
- Registrierung ist standardmaessig aktiviert (erster User = Admin) - Einmal-Registrierungslinks: Admin kann Links generieren die auch bei deaktivierter Registrierung funktionieren, nach Nutzung ungueltig - Optional Link per System-Email versenden - System-SMTP in Admin-Einstellungen konfigurierbar: Server, Port, SSL, Benutzername, Passwort, Absender-Adresse - SMTP-Verbindungstest-Button - Register-Seite akzeptiert ?invite=TOKEN aus der URL Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
+17
-3
@@ -76,7 +76,7 @@ def registration_status():
|
|||||||
"""Check if public registration is allowed."""
|
"""Check if public registration is allowed."""
|
||||||
from app.models.settings import AppSettings
|
from app.models.settings import AppSettings
|
||||||
is_first_user = User.query.count() == 0
|
is_first_user = User.query.count() == 0
|
||||||
public_registration = AppSettings.get_bool('public_registration', default=False)
|
public_registration = AppSettings.get_bool('public_registration', default=True)
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'allowed': is_first_user or public_registration,
|
'allowed': is_first_user or public_registration,
|
||||||
'is_first_user': is_first_user,
|
'is_first_user': is_first_user,
|
||||||
@@ -87,9 +87,19 @@ def registration_status():
|
|||||||
def register():
|
def register():
|
||||||
from app.models.settings import AppSettings
|
from app.models.settings import AppSettings
|
||||||
|
|
||||||
# Check if registration is allowed
|
|
||||||
is_first_user = User.query.count() == 0
|
is_first_user = User.query.count() == 0
|
||||||
if not is_first_user and not AppSettings.get_bool('public_registration', default=False):
|
|
||||||
|
# Check invite token (works even if public registration is off)
|
||||||
|
invite_token = request.args.get('invite') or (request.get_json() or {}).get('invite_token')
|
||||||
|
valid_invite = False
|
||||||
|
if invite_token:
|
||||||
|
from app.models.settings import AppSettings as _S
|
||||||
|
stored = _S.get(f'invite_{invite_token}', '')
|
||||||
|
if stored == 'valid':
|
||||||
|
valid_invite = True
|
||||||
|
|
||||||
|
# Check if registration is allowed
|
||||||
|
if not is_first_user and not valid_invite and not AppSettings.get_bool('public_registration', default=True):
|
||||||
return jsonify({'error': 'Oeffentliche Registrierung ist deaktiviert'}), 403
|
return jsonify({'error': 'Oeffentliche Registrierung ist deaktiviert'}), 403
|
||||||
|
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
@@ -129,6 +139,10 @@ def register():
|
|||||||
db.session.add(user)
|
db.session.add(user)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
# Invalidate invite token if used
|
||||||
|
if valid_invite and invite_token:
|
||||||
|
AppSettings.set(f'invite_{invite_token}', 'used')
|
||||||
|
|
||||||
access_token = create_access_token(user.id)
|
access_token = create_access_token(user.id)
|
||||||
refresh_token = create_refresh_token(user.id)
|
refresh_token = create_refresh_token(user.id)
|
||||||
|
|
||||||
|
|||||||
+108
-1
@@ -138,7 +138,13 @@ def delete_user(user_id):
|
|||||||
@admin_required
|
@admin_required
|
||||||
def get_settings():
|
def get_settings():
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'public_registration': AppSettings.get_bool('public_registration', default=False),
|
'public_registration': AppSettings.get_bool('public_registration', default=True),
|
||||||
|
'system_smtp_host': AppSettings.get('system_smtp_host', ''),
|
||||||
|
'system_smtp_port': int(AppSettings.get('system_smtp_port', '587')),
|
||||||
|
'system_smtp_ssl': AppSettings.get_bool('system_smtp_ssl', default=True),
|
||||||
|
'system_smtp_username': AppSettings.get('system_smtp_username', ''),
|
||||||
|
'system_smtp_password_set': bool(AppSettings.get('system_smtp_password', '')),
|
||||||
|
'system_email_from': AppSettings.get('system_email_from', ''),
|
||||||
}), 200
|
}), 200
|
||||||
|
|
||||||
|
|
||||||
@@ -148,9 +154,110 @@ def update_settings():
|
|||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
if 'public_registration' in data:
|
if 'public_registration' in data:
|
||||||
AppSettings.set('public_registration', str(data['public_registration']).lower())
|
AppSettings.set('public_registration', str(data['public_registration']).lower())
|
||||||
|
for key in ['system_smtp_host', 'system_smtp_port', 'system_smtp_ssl',
|
||||||
|
'system_smtp_username', 'system_email_from']:
|
||||||
|
if key in data:
|
||||||
|
AppSettings.set(key, str(data[key]))
|
||||||
|
if 'system_smtp_password' in data and data['system_smtp_password']:
|
||||||
|
AppSettings.set('system_smtp_password', data['system_smtp_password'])
|
||||||
return jsonify({'message': 'Einstellungen gespeichert'}), 200
|
return jsonify({'message': 'Einstellungen gespeichert'}), 200
|
||||||
|
|
||||||
|
|
||||||
|
@api_bp.route('/settings/test-email', methods=['POST'])
|
||||||
|
@admin_required
|
||||||
|
def test_system_email():
|
||||||
|
"""Test system SMTP connection."""
|
||||||
|
import smtplib
|
||||||
|
|
||||||
|
host = AppSettings.get('system_smtp_host', '')
|
||||||
|
port = int(AppSettings.get('system_smtp_port', '587'))
|
||||||
|
ssl = AppSettings.get_bool('system_smtp_ssl', default=True)
|
||||||
|
username = AppSettings.get('system_smtp_username', '')
|
||||||
|
password = AppSettings.get('system_smtp_password', '')
|
||||||
|
from_addr = AppSettings.get('system_email_from', '')
|
||||||
|
|
||||||
|
if not host or not username or not password:
|
||||||
|
return jsonify({'error': 'SMTP-Einstellungen unvollstaendig'}), 400
|
||||||
|
|
||||||
|
try:
|
||||||
|
if ssl and port == 465:
|
||||||
|
server = smtplib.SMTP_SSL(host, port, timeout=10)
|
||||||
|
else:
|
||||||
|
server = smtplib.SMTP(host, port, timeout=10)
|
||||||
|
server.starttls()
|
||||||
|
server.login(username, password)
|
||||||
|
server.quit()
|
||||||
|
return jsonify({'message': 'SMTP-Verbindung erfolgreich'}), 200
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({'error': f'Verbindungsfehler: {str(e)}'}), 400
|
||||||
|
|
||||||
|
|
||||||
|
# --- Invite Links ---
|
||||||
|
|
||||||
|
@api_bp.route('/settings/invite', methods=['POST'])
|
||||||
|
@admin_required
|
||||||
|
def create_invite_link():
|
||||||
|
"""Generate a one-time registration link."""
|
||||||
|
import secrets
|
||||||
|
token = secrets.token_urlsafe(32)
|
||||||
|
AppSettings.set(f'invite_{token}', 'valid')
|
||||||
|
|
||||||
|
data = request.get_json() or {}
|
||||||
|
send_email = data.get('send_to_email', '')
|
||||||
|
|
||||||
|
result = {
|
||||||
|
'token': token,
|
||||||
|
'url': f'/register?invite={token}',
|
||||||
|
}
|
||||||
|
|
||||||
|
# Optionally send via system email
|
||||||
|
if send_email:
|
||||||
|
import smtplib
|
||||||
|
from email.mime.text import MIMEText
|
||||||
|
|
||||||
|
host = AppSettings.get('system_smtp_host', '')
|
||||||
|
port = int(AppSettings.get('system_smtp_port', '587'))
|
||||||
|
ssl = AppSettings.get_bool('system_smtp_ssl', default=True)
|
||||||
|
username = AppSettings.get('system_smtp_username', '')
|
||||||
|
password = AppSettings.get('system_smtp_password', '')
|
||||||
|
from_addr = AppSettings.get('system_email_from', '')
|
||||||
|
|
||||||
|
if not host or not password:
|
||||||
|
return jsonify({**result, 'email_sent': False,
|
||||||
|
'email_error': 'System-Email nicht konfiguriert'}), 200
|
||||||
|
|
||||||
|
# Build full URL from request
|
||||||
|
base_url = request.host_url.rstrip('/')
|
||||||
|
full_url = f'{base_url}/register?invite={token}'
|
||||||
|
|
||||||
|
body = (
|
||||||
|
f'Du wurdest zur Mini-Cloud eingeladen!\n\n'
|
||||||
|
f'Klicke auf folgenden Link, um dich zu registrieren:\n'
|
||||||
|
f'{full_url}\n\n'
|
||||||
|
f'Dieser Link ist nur einmal verwendbar.'
|
||||||
|
)
|
||||||
|
msg = MIMEText(body, 'plain', 'utf-8')
|
||||||
|
msg['From'] = from_addr or username
|
||||||
|
msg['To'] = send_email
|
||||||
|
msg['Subject'] = 'Einladung zur Mini-Cloud'
|
||||||
|
|
||||||
|
try:
|
||||||
|
if ssl and port == 465:
|
||||||
|
server = smtplib.SMTP_SSL(host, port, timeout=10)
|
||||||
|
else:
|
||||||
|
server = smtplib.SMTP(host, port, timeout=10)
|
||||||
|
server.starttls()
|
||||||
|
server.login(username, password)
|
||||||
|
server.sendmail(from_addr or username, [send_email], msg.as_string())
|
||||||
|
server.quit()
|
||||||
|
result['email_sent'] = True
|
||||||
|
except Exception as e:
|
||||||
|
result['email_sent'] = False
|
||||||
|
result['email_error'] = str(e)
|
||||||
|
|
||||||
|
return jsonify(result), 201
|
||||||
|
|
||||||
|
|
||||||
# --- User search (for sharing dialogs) ---
|
# --- User search (for sharing dialogs) ---
|
||||||
|
|
||||||
@api_bp.route('/users/search', methods=['GET'])
|
@api_bp.route('/users/search', methods=['GET'])
|
||||||
|
|||||||
@@ -19,9 +19,10 @@ export const useAuthStore = defineStore('auth', () => {
|
|||||||
return response.data
|
return response.data
|
||||||
}
|
}
|
||||||
|
|
||||||
async function register(username, password, email) {
|
async function register(username, password, email, inviteToken) {
|
||||||
const payload = { username, password }
|
const payload = { username, password }
|
||||||
if (email) payload.email = email
|
if (email) payload.email = email
|
||||||
|
if (inviteToken) payload.invite_token = inviteToken
|
||||||
const response = await apiClient.post('/auth/register', payload)
|
const response = await apiClient.post('/auth/register', payload)
|
||||||
user.value = response.data.user
|
user.value = response.data.user
|
||||||
accessToken.value = response.data.access_token
|
accessToken.value = response.data.access_token
|
||||||
|
|||||||
@@ -6,14 +6,73 @@
|
|||||||
|
|
||||||
<!-- App Settings -->
|
<!-- App Settings -->
|
||||||
<div class="admin-section">
|
<div class="admin-section">
|
||||||
<h3>Einstellungen</h3>
|
<h3>Registrierung</h3>
|
||||||
<div class="setting-row">
|
<div class="setting-row">
|
||||||
<div class="setting-info">
|
<div class="setting-info">
|
||||||
<strong>Oeffentliche Registrierung</strong>
|
<strong>Oeffentliche Registrierung</strong>
|
||||||
<p>Wenn aktiviert, koennen sich neue Benutzer selbst registrieren. Andernfalls kann nur ein Admin neue Benutzer anlegen.</p>
|
<p>Wenn aktiviert, koennen sich neue Benutzer selbst registrieren. Andernfalls kann nur ein Admin neue Benutzer anlegen oder Einladungslinks versenden.</p>
|
||||||
</div>
|
</div>
|
||||||
<InputSwitch v-model="publicRegistration" @change="saveSettings" />
|
<InputSwitch v-model="publicRegistration" @change="saveSettings" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="invite-section">
|
||||||
|
<h4>Einladungslink generieren</h4>
|
||||||
|
<p class="hint">Einmal-Links funktionieren auch bei deaktivierter oeffentlicher Registrierung.</p>
|
||||||
|
<div class="invite-row">
|
||||||
|
<InputText v-model="inviteEmail" placeholder="E-Mail zum Versenden (optional)" fluid />
|
||||||
|
<Button label="Link erstellen" icon="pi pi-link" size="small" @click="createInvite" :loading="inviteLoading" />
|
||||||
|
</div>
|
||||||
|
<div v-if="inviteResult" class="invite-result">
|
||||||
|
<div class="invite-url">
|
||||||
|
<code>{{ fullInviteUrl }}</code>
|
||||||
|
<Button icon="pi pi-copy" text size="small" @click="copyInviteLink" />
|
||||||
|
</div>
|
||||||
|
<Message v-if="inviteResult.email_sent" severity="success" :closable="false">
|
||||||
|
Einladung an {{ inviteEmail }} gesendet
|
||||||
|
</Message>
|
||||||
|
<Message v-if="inviteResult.email_sent === false" severity="warn" :closable="false">
|
||||||
|
Link erstellt, aber E-Mail konnte nicht gesendet werden: {{ inviteResult.email_error }}
|
||||||
|
</Message>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- System Email -->
|
||||||
|
<div class="admin-section">
|
||||||
|
<h3>System-E-Mail (SMTP)</h3>
|
||||||
|
<p class="hint">Wird fuer Einladungslinks und System-Benachrichtigungen verwendet.</p>
|
||||||
|
<div class="smtp-form">
|
||||||
|
<div class="field-row">
|
||||||
|
<div class="field flex-grow">
|
||||||
|
<label>SMTP-Server</label>
|
||||||
|
<InputText v-model="smtpForm.system_smtp_host" placeholder="smtp.beispiel.de" fluid />
|
||||||
|
</div>
|
||||||
|
<div class="field" style="width: 100px">
|
||||||
|
<label>Port</label>
|
||||||
|
<InputText v-model.number="smtpForm.system_smtp_port" type="number" fluid />
|
||||||
|
</div>
|
||||||
|
<div class="field" style="width: 80px">
|
||||||
|
<label>SSL</label>
|
||||||
|
<InputSwitch v-model="smtpForm.system_smtp_ssl" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label>Benutzername</label>
|
||||||
|
<InputText v-model="smtpForm.system_smtp_username" fluid />
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label>Passwort {{ smtpPasswordSet ? '(gesetzt - leer lassen = nicht aendern)' : '' }}</label>
|
||||||
|
<Password v-model="smtpForm.system_smtp_password" :feedback="false" toggle-mask fluid />
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label>Absender-Adresse</label>
|
||||||
|
<InputText v-model="smtpForm.system_email_from" placeholder="noreply@beispiel.de" fluid />
|
||||||
|
</div>
|
||||||
|
<div class="smtp-actions">
|
||||||
|
<Button label="Speichern" icon="pi pi-save" size="small" @click="saveSmtp" />
|
||||||
|
<Button label="Verbindung testen" icon="pi pi-check-circle" size="small" outlined @click="testSmtp" :loading="smtpTesting" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- User Management -->
|
<!-- User Management -->
|
||||||
@@ -94,7 +153,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted } from 'vue'
|
import { ref, computed, onMounted } from 'vue'
|
||||||
import { useToast } from 'primevue/usetoast'
|
import { useToast } from 'primevue/usetoast'
|
||||||
import apiClient from '../api/client'
|
import apiClient from '../api/client'
|
||||||
import DataTable from 'primevue/datatable'
|
import DataTable from 'primevue/datatable'
|
||||||
@@ -111,7 +170,21 @@ import Message from 'primevue/message'
|
|||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
const users = ref([])
|
const users = ref([])
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const publicRegistration = ref(false)
|
const publicRegistration = ref(true)
|
||||||
|
|
||||||
|
// Invite links
|
||||||
|
const inviteEmail = ref('')
|
||||||
|
const inviteLoading = ref(false)
|
||||||
|
const inviteResult = ref(null)
|
||||||
|
const fullInviteUrl = computed(() => inviteResult.value ? `${window.location.origin}${inviteResult.value.url}` : '')
|
||||||
|
|
||||||
|
// System SMTP
|
||||||
|
const smtpForm = ref({
|
||||||
|
system_smtp_host: '', system_smtp_port: 587, system_smtp_ssl: true,
|
||||||
|
system_smtp_username: '', system_smtp_password: '', system_email_from: '',
|
||||||
|
})
|
||||||
|
const smtpPasswordSet = ref(false)
|
||||||
|
const smtpTesting = ref(false)
|
||||||
|
|
||||||
const showUserDialog = ref(false)
|
const showUserDialog = ref(false)
|
||||||
const editingUser = ref(null)
|
const editingUser = ref(null)
|
||||||
@@ -139,7 +212,13 @@ async function loadSettings() {
|
|||||||
try {
|
try {
|
||||||
const res = await apiClient.get('/settings')
|
const res = await apiClient.get('/settings')
|
||||||
publicRegistration.value = res.data.public_registration
|
publicRegistration.value = res.data.public_registration
|
||||||
} catch { /* first load, default false */ }
|
smtpForm.value.system_smtp_host = res.data.system_smtp_host || ''
|
||||||
|
smtpForm.value.system_smtp_port = res.data.system_smtp_port || 587
|
||||||
|
smtpForm.value.system_smtp_ssl = res.data.system_smtp_ssl
|
||||||
|
smtpForm.value.system_smtp_username = res.data.system_smtp_username || ''
|
||||||
|
smtpForm.value.system_email_from = res.data.system_email_from || ''
|
||||||
|
smtpPasswordSet.value = res.data.system_smtp_password_set
|
||||||
|
} catch { /* first load, defaults */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveSettings() {
|
async function saveSettings() {
|
||||||
@@ -151,6 +230,54 @@ async function saveSettings() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Invite links ---
|
||||||
|
async function createInvite() {
|
||||||
|
inviteLoading.value = true
|
||||||
|
inviteResult.value = null
|
||||||
|
try {
|
||||||
|
const payload = {}
|
||||||
|
if (inviteEmail.value.trim()) payload.send_to_email = inviteEmail.value.trim()
|
||||||
|
const res = await apiClient.post('/settings/invite', payload)
|
||||||
|
inviteResult.value = res.data
|
||||||
|
toast.add({ severity: 'success', summary: 'Einladungslink erstellt', life: 3000 })
|
||||||
|
} catch (err) {
|
||||||
|
toast.add({ severity: 'error', summary: 'Fehler', detail: err.response?.data?.error, life: 5000 })
|
||||||
|
} finally {
|
||||||
|
inviteLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function copyInviteLink() {
|
||||||
|
navigator.clipboard.writeText(fullInviteUrl.value)
|
||||||
|
toast.add({ severity: 'info', summary: 'Link kopiert', life: 2000 })
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- System SMTP ---
|
||||||
|
async function saveSmtp() {
|
||||||
|
try {
|
||||||
|
await apiClient.put('/settings', smtpForm.value)
|
||||||
|
toast.add({ severity: 'success', summary: 'SMTP-Einstellungen gespeichert', life: 3000 })
|
||||||
|
await loadSettings()
|
||||||
|
} catch (err) {
|
||||||
|
toast.add({ severity: 'error', summary: 'Fehler', detail: err.response?.data?.error, life: 5000 })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function testSmtp() {
|
||||||
|
smtpTesting.value = true
|
||||||
|
try {
|
||||||
|
// Save first, then test
|
||||||
|
await apiClient.put('/settings', smtpForm.value)
|
||||||
|
const res = await apiClient.post('/settings/test-email')
|
||||||
|
toast.add({ severity: 'success', summary: res.data.message, life: 5000 })
|
||||||
|
} catch (err) {
|
||||||
|
toast.add({ severity: 'error', summary: 'SMTP-Test fehlgeschlagen', detail: err.response?.data?.error, life: 8000 })
|
||||||
|
} finally {
|
||||||
|
smtpTesting.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Users ---
|
||||||
function openNewUser() {
|
function openNewUser() {
|
||||||
editingUser.value = null
|
editingUser.value = null
|
||||||
userForm.value = { username: '', email: '', password: '', role: 'user', storage_quota_mb: 5120, is_active: true }
|
userForm.value = { username: '', email: '', password: '', role: 'user', storage_quota_mb: 5120, is_active: true }
|
||||||
@@ -240,4 +367,15 @@ onMounted(() => {
|
|||||||
.setting-info p { margin: 0; font-size: 0.85rem; color: var(--p-text-muted-color); }
|
.setting-info p { margin: 0; font-size: 0.85rem; color: var(--p-text-muted-color); }
|
||||||
.field { margin-bottom: 1rem; }
|
.field { margin-bottom: 1rem; }
|
||||||
.field label { display: block; margin-bottom: 0.5rem; font-weight: 500; font-size: 0.875rem; }
|
.field label { display: block; margin-bottom: 0.5rem; font-weight: 500; font-size: 0.875rem; }
|
||||||
|
.field-row { display: flex; gap: 0.75rem; align-items: flex-end; }
|
||||||
|
.flex-grow { flex: 1; }
|
||||||
|
.hint { font-size: 0.85rem; color: var(--p-text-muted-color); margin: 0 0 0.75rem; }
|
||||||
|
.invite-section { margin-top: 1.5rem; padding-top: 1rem; border-top: 1px solid var(--p-surface-200); }
|
||||||
|
.invite-section h4 { margin: 0 0 0.25rem; font-size: 0.95rem; }
|
||||||
|
.invite-row { display: flex; gap: 0.5rem; align-items: flex-start; }
|
||||||
|
.invite-result { margin-top: 0.75rem; }
|
||||||
|
.invite-url { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.5rem; }
|
||||||
|
.invite-url code { font-size: 0.8rem; word-break: break-all; background: var(--p-surface-100); padding: 0.375rem 0.5rem; border-radius: 4px; }
|
||||||
|
.smtp-form { max-width: 550px; }
|
||||||
|
.smtp-actions { display: flex; gap: 0.5rem; }
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -72,7 +72,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
import { useAuthStore } from '../stores/auth'
|
import { useAuthStore } from '../stores/auth'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import InputText from 'primevue/inputtext'
|
import InputText from 'primevue/inputtext'
|
||||||
@@ -81,6 +81,7 @@ import Button from 'primevue/button'
|
|||||||
import Message from 'primevue/message'
|
import Message from 'primevue/message'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
const route = useRoute()
|
||||||
const auth = useAuthStore()
|
const auth = useAuthStore()
|
||||||
|
|
||||||
const username = ref('')
|
const username = ref('')
|
||||||
@@ -89,8 +90,13 @@ const password = ref('')
|
|||||||
const password2 = ref('')
|
const password2 = ref('')
|
||||||
const error = ref('')
|
const error = ref('')
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
const inviteToken = ref('')
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
// Check for invite token in URL
|
||||||
|
inviteToken.value = route.query.invite || ''
|
||||||
|
if (inviteToken.value) return // Invite links always work
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await axios.get('/api/auth/registration-status')
|
const res = await axios.get('/api/auth/registration-status')
|
||||||
if (!res.data.allowed) {
|
if (!res.data.allowed) {
|
||||||
@@ -109,7 +115,7 @@ async function handleRegister() {
|
|||||||
|
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
await auth.register(username.value, password.value, email.value || undefined)
|
await auth.register(username.value, password.value, email.value || undefined, inviteToken.value || undefined)
|
||||||
router.push('/')
|
router.push('/')
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
error.value = err.response?.data?.error || 'Registrierung fehlgeschlagen'
|
error.value = err.response?.data?.error || 'Registrierung fehlgeschlagen'
|
||||||
|
|||||||
Reference in New Issue
Block a user