fixes
Build And Push Image / docker (push) Successful in 3m5s Details

This commit is contained in:
Matt 2025-08-08 20:07:47 +02:00
parent 4365cc53ff
commit 72492fb754
2 changed files with 275 additions and 0 deletions

View File

@ -112,6 +112,55 @@
</v-col>
</v-row>
<!-- System Configuration -->
<v-row class="mb-6">
<v-col cols="12" md="6">
<v-card elevation="2">
<v-card-title>
<v-icon left>mdi-shield-check</v-icon>
reCAPTCHA Configuration
</v-card-title>
<v-card-text>
<p class="mb-4">Configure reCAPTCHA settings for form security.</p>
<v-btn
color="warning"
variant="outlined"
block
size="large"
@click="showRecaptchaConfig = true"
>
<v-icon start>mdi-shield-account</v-icon>
Configure reCAPTCHA
</v-btn>
</v-card-text>
</v-card>
</v-col>
<v-col cols="12" md="6">
<v-card elevation="2">
<v-card-title>
<v-icon left>mdi-account-plus</v-icon>
Membership Configuration
</v-card-title>
<v-card-text>
<p class="mb-4">Configure membership fees and payment details.</p>
<v-btn
color="success"
variant="outlined"
block
size="large"
@click="showMembershipConfig = true"
>
<v-icon start>mdi-bank</v-icon>
Configure Membership
</v-btn>
</v-card-text>
</v-card>
</v-col>
</v-row>
<!-- NocoDB Configuration -->
<v-row class="mb-6">
<v-col cols="12">
@ -165,6 +214,120 @@
@settings-saved="handleAdminConfigSaved"
/>
<!-- reCAPTCHA Configuration Dialog -->
<v-dialog v-model="showRecaptchaConfig" max-width="600">
<v-card>
<v-card-title class="text-h5">
<v-icon left>mdi-shield-account</v-icon>
reCAPTCHA Configuration
</v-card-title>
<v-card-text>
<v-alert type="info" variant="tonal" class="mb-4">
<v-alert-title>Security Configuration</v-alert-title>
Configure Google reCAPTCHA settings for form protection on the registration page.
</v-alert>
<v-form ref="recaptchaForm" v-model="recaptchaValid">
<v-text-field
v-model="recaptchaConfig.siteKey"
label="Site Key"
placeholder="6Lc6BAAAAAAAAChqRbQZcn_yyyyyyyyyyyyyyyyy"
:rules="[v => !!v || 'Site key is required']"
variant="outlined"
required
/>
<v-text-field
v-model="recaptchaConfig.secretKey"
label="Secret Key"
placeholder="6Lc6BAAAAAAAAKN3DRm6VA_xxxxxxxxxxxxxxxxx"
:rules="[v => !!v || 'Secret key is required']"
variant="outlined"
type="password"
required
/>
<v-alert type="warning" variant="tonal" class="mt-4">
<v-alert-title>Important</v-alert-title>
Keep your secret key confidential. You can get these keys from the Google reCAPTCHA admin console.
</v-alert>
</v-form>
</v-card-text>
<v-card-actions>
<v-spacer />
<v-btn text @click="showRecaptchaConfig = false">Cancel</v-btn>
<v-btn
color="primary"
:loading="savingRecaptcha"
:disabled="!recaptchaValid"
@click="saveRecaptchaConfig"
>
Save Configuration
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<!-- Membership Configuration Dialog -->
<v-dialog v-model="showMembershipConfig" max-width="600">
<v-card>
<v-card-title class="text-h5">
<v-icon left>mdi-bank</v-icon>
Membership Configuration
</v-card-title>
<v-card-text>
<v-alert type="info" variant="tonal" class="mb-4">
<v-alert-title>Payment Configuration</v-alert-title>
Configure membership fees and payment details displayed on the registration page.
</v-alert>
<v-form ref="membershipForm" v-model="membershipValid">
<v-text-field
v-model="membershipConfig.membershipFee"
label="Annual Membership Fee (€)"
type="number"
:rules="[
v => !!v || 'Membership fee is required',
v => v > 0 || 'Fee must be greater than 0'
]"
variant="outlined"
required
/>
<v-text-field
v-model="membershipConfig.iban"
label="IBAN"
placeholder="DE89 3704 0044 0532 0130 00"
:rules="[v => !!v || 'IBAN is required']"
variant="outlined"
required
/>
<v-text-field
v-model="membershipConfig.accountHolder"
label="Account Holder Name"
placeholder="MonacoUSA Association"
:rules="[v => !!v || 'Account holder is required']"
variant="outlined"
required
/>
</v-form>
</v-card-text>
<v-card-actions>
<v-spacer />
<v-btn text @click="showMembershipConfig = false">Cancel</v-btn>
<v-btn
color="primary"
:loading="savingMembership"
:disabled="!membershipValid"
@click="saveMembershipConfig"
>
Save Configuration
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<!-- Create User Dialog -->
<v-dialog v-model="showCreateUserDialog" max-width="600">
<v-card>
@ -253,6 +416,8 @@ const userCount = ref(0);
const loading = ref(false);
const showCreateUserDialog = ref(false);
const showAdminConfig = ref(false);
const showRecaptchaConfig = ref(false);
const showMembershipConfig = ref(false);
// Create user dialog data
const createUserValid = ref(false);
@ -270,6 +435,23 @@ const roleOptions = [
{ title: 'Administrator', value: 'admin' }
];
// reCAPTCHA configuration data
const recaptchaValid = ref(false);
const savingRecaptcha = ref(false);
const recaptchaConfig = ref({
siteKey: '',
secretKey: ''
});
// Membership configuration data
const membershipValid = ref(false);
const savingMembership = ref(false);
const membershipConfig = ref({
membershipFee: 50,
iban: '',
accountHolder: ''
});
const recentActivity = ref([
{
id: 1,
@ -349,6 +531,59 @@ const handleAdminConfigSaved = () => {
showAdminConfig.value = false;
};
const saveRecaptchaConfig = async () => {
if (!recaptchaValid.value) return;
savingRecaptcha.value = true;
try {
const response = await $fetch('/api/admin/recaptcha-config', {
method: 'POST',
body: {
siteKey: recaptchaConfig.value.siteKey,
secretKey: recaptchaConfig.value.secretKey
}
}) as any;
if (response?.success) {
showRecaptchaConfig.value = false;
console.log('reCAPTCHA configuration saved successfully');
// TODO: Show success notification
}
} catch (error) {
console.error('Failed to save reCAPTCHA configuration:', error);
// TODO: Show error notification
} finally {
savingRecaptcha.value = false;
}
};
const saveMembershipConfig = async () => {
if (!membershipValid.value) return;
savingMembership.value = true;
try {
const response = await $fetch('/api/admin/registration-config', {
method: 'POST',
body: {
membershipFee: membershipConfig.value.membershipFee,
iban: membershipConfig.value.iban,
accountHolder: membershipConfig.value.accountHolder
}
}) as any;
if (response?.success) {
showMembershipConfig.value = false;
console.log('Membership configuration saved successfully');
// TODO: Show success notification
}
} catch (error) {
console.error('Failed to save membership configuration:', error);
// TODO: Show error notification
} finally {
savingMembership.value = false;
}
};
const createUserAccount = async () => {
if (!createUserValid.value) return;

View File

@ -150,8 +150,11 @@
@edit="editMember"
@delete="confirmDeleteMember"
@view="viewMember"
@create-portal-account="createPortalAccount"
:can-edit="canEditMembers"
:can-delete="canDeleteMembers"
:can-create-portal-account="canCreatePortalAccounts"
:creating-portal-account="creatingPortalAccountIds.includes(member.Id)"
/>
</v-col>
@ -258,6 +261,7 @@ const { firstName, isBoard, isAdmin } = useAuth();
const canCreateMembers = computed(() => isBoard.value || isAdmin.value);
const canEditMembers = computed(() => isBoard.value || isAdmin.value);
const canDeleteMembers = computed(() => isAdmin.value);
const canCreatePortalAccounts = computed(() => isAdmin.value); // Only admins can create portal accounts
// Reactive data
const members = ref<Member[]>([]);
@ -282,6 +286,9 @@ const deleteLoading = ref(false);
const showSuccess = ref(false);
const successMessage = ref('');
// Portal account creation
const creatingPortalAccountIds = ref<string[]>([]);
// Filter options
const activeFilterOptions = [
{ title: 'Active Members', value: 'active' },
@ -493,6 +500,39 @@ const handleMemberUpdated = (updatedMember: Member) => {
successMessage.value = `${updatedMember.FullName} has been updated successfully.`;
};
const createPortalAccount = async (member: Member) => {
if (!member.Id || creatingPortalAccountIds.value.includes(member.Id)) return;
// Add to creating array to show loading state
creatingPortalAccountIds.value.push(member.Id);
try {
const response = await $fetch(`/api/members/${member.Id}/create-portal-account`) as any;
if (response?.success) {
// Update the member in the local array to reflect the new keycloak_id
const index = members.value.findIndex(m => m.Id === member.Id);
if (index !== -1) {
members.value[index] = { ...members.value[index], keycloak_id: response.keycloak_id };
}
showSuccess.value = true;
successMessage.value = `Portal account created successfully for ${member.FullName}. They will receive an email with login instructions.`;
} else {
throw new Error(response?.message || 'Failed to create portal account');
}
} catch (err: any) {
console.error('Error creating portal account:', err);
error.value = err.data?.message || err.message || 'Failed to create portal account. Please try again.';
} finally {
// Remove from creating array
const index = creatingPortalAccountIds.value.indexOf(member.Id);
if (index > -1) {
creatingPortalAccountIds.value.splice(index, 1);
}
}
};
// Load members on mount
onMounted(() => {
loadMembers();