Merge pull request 'patches v1.5.1' (#93) from develop into main

Reviewed-on: #93
This commit is contained in:
Julian Krauser 2025-05-07 07:29:01 +00:00
commit 9bac6e2f97
44 changed files with 100 additions and 90 deletions

View file

@ -8,12 +8,9 @@ Dieses Repository dient hauptsächlich zur Verwaltung der Mitgliederdaten, aber
Eine Demo dieser Seite finden Sie unter [https://admin-demo.ff-admin.de](https://admin-demo.ff-admin.de). Eine Demo dieser Seite finden Sie unter [https://admin-demo.ff-admin.de](https://admin-demo.ff-admin.de).
Für die Verwendung muss ein TOTP-Code eingegeben werden. Die Zugangsdaten (Lesebeschränkt) sind unterhalb dem Login angegeben.
Die Zugangsdaten (Lesebeschränkt) sind:\ Das Handbuch zur Anwendung finden sie unter [https://ff-admin.de/ff-admin-handbook](https://ff-admin.de/ff-admin-handbook).
EMAIL: demo-besucher\
TOTP: ![alt text](demo-totp-qrcode.png)\
TOTP-Code: FBMDAJKFOYQXM2DNH47GWWBGJ5KWOUCW
## Installation ## Installation
@ -29,17 +26,9 @@ services:
image: docker.registry.jk-effects.cloud/ehrenamt/ff-admin/app:latest image: docker.registry.jk-effects.cloud/ehrenamt/ff-admin/app:latest
container_name: ff_admin container_name: ff_admin
restart: unless-stopped restart: unless-stopped
#environment: #environment:
# - SERVERADDRESS=<backend_url (https://... | http://...)> # wichtig: ohne Pfad # - SERVERADDRESS=<backend_url (https://... | http://...)> # wichtig: ohne Pfad
# - APPNAMEOVERWRITE=<appname> # ersetzt den Namen FF-Admin auf der Login-Seite und sonstigen Positionen in der Oberfläche
# - IMPRINTLINK=<imprint link>
# - PRIVACYLINK=<privacy link>
# - CUSTOMLOGINMESSAGE=betrieben von xy
#volumes:
# - <volume|local path>/favicon.ico:/usr/share/nginx/html/favicon.ico # 48x48 px Auflösung
# - <volume|local path>/favicon.png:/usr/share/nginx/html/favicon.png # 512x512 px Auflösung - wird als pwa Icon genutzt
# - <volume|local path>/Logo.png:/usr/share/nginx/html/Logo.png
``` ```
Wenn keine Server-Adresse angegeben wird, wird versucht das Backend unter der URL des Frontends zu erreichen. Dazu muss das Backend auf der gleichen URL wie das Frontend laufen. Zur Unterscheidung von Frontend und Backend bei gleicher URL müssen alle Anfragen mit dem PathPrefix `/api` an das Backend weitergeleitet werden. Wenn keine Server-Adresse angegeben wird, wird versucht das Backend unter der URL des Frontends zu erreichen. Dazu muss das Backend auf der gleichen URL wie das Frontend laufen. Zur Unterscheidung von Frontend und Backend bei gleicher URL müssen alle Anfragen mit dem PathPrefix `/api` an das Backend weitergeleitet werden.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

View file

@ -42,7 +42,7 @@ import UserMenu from "./UserMenu.vue";
<script lang="ts"> <script lang="ts">
import { defineComponent } from "vue"; import { defineComponent } from "vue";
import AppLogo from "./AppLogo.vue"; import AppLogo from "./AppLogo.vue";
import { useConfigurationStore } from "../stores/configuration"; import { useConfigurationStore } from "@/stores/configuration";
export default defineComponent({ export default defineComponent({
computed: { computed: {
...mapState(useAuthStore, ["authCheck"]), ...mapState(useAuthStore, ["authCheck"]),

View file

@ -48,7 +48,7 @@ import Spinner from "@/components/Spinner.vue";
import SuccessCheckmark from "@/components/SuccessCheckmark.vue"; import SuccessCheckmark from "@/components/SuccessCheckmark.vue";
import FailureXMark from "@/components/FailureXMark.vue"; import FailureXMark from "@/components/FailureXMark.vue";
import TextCopy from "@/components/TextCopy.vue"; import TextCopy from "@/components/TextCopy.vue";
import { hashString } from "../../helpers/crypto"; import { hashString } from "@/helpers/crypto";
</script> </script>
<script lang="ts"> <script lang="ts">
@ -84,7 +84,7 @@ export default defineComponent({
this.changeStatus = "loading"; this.changeStatus = "loading";
this.changeError = ""; this.changeError = "";
this.$http this.$http
.post(`/user/changeToPW`, { .patch(`/user/changeToPW`, {
newpassword: await hashString(formData.new.value), newpassword: await hashString(formData.new.value),
}) })
.then((result) => { .then((result) => {

View file

@ -69,7 +69,7 @@ export default defineComponent({
this.verifyStatus = "loading"; this.verifyStatus = "loading";
this.verifyError = ""; this.verifyError = "";
this.$http this.$http
.post(`/user/changeToTOTP`, { .patch(`/user/changeToTOTP`, {
otp: this.otp, otp: this.otp,
totp: formData.totp.value, totp: formData.totp.value,
}) })

View file

@ -58,7 +58,7 @@ import MainTemplate from "@/templates/Main.vue";
import Spinner from "@/components/Spinner.vue"; import Spinner from "@/components/Spinner.vue";
import SuccessCheckmark from "@/components/SuccessCheckmark.vue"; import SuccessCheckmark from "@/components/SuccessCheckmark.vue";
import FailureXMark from "@/components/FailureXMark.vue"; import FailureXMark from "@/components/FailureXMark.vue";
import { hashString } from "../../helpers/crypto"; import { hashString } from "@/helpers/crypto";
</script> </script>
<script lang="ts"> <script lang="ts">
@ -87,7 +87,7 @@ export default defineComponent({
this.changeStatus = "loading"; this.changeStatus = "loading";
this.changeError = ""; this.changeError = "";
this.$http this.$http
.post(`/user/changepw`, { .patch(`/user/changepw`, {
current: await hashString(formData.current.value), current: await hashString(formData.current.value),
newpassword: await hashString(formData.new.value), newpassword: await hashString(formData.new.value),
}) })

View file

@ -87,7 +87,7 @@ import { CheckIcon, ChevronUpDownIcon } from "@heroicons/vue/20/solid";
import { useMemberStore } from "@/stores/admin/club/member/member"; import { useMemberStore } from "@/stores/admin/club/member/member";
import type { MemberViewModel } from "@/viewmodels/admin/club/member/member.models"; import type { MemberViewModel } from "@/viewmodels/admin/club/member/member.models";
import difference from "lodash.difference"; import difference from "lodash.difference";
import Spinner from "../Spinner.vue"; import Spinner from "@/components/Spinner.vue";
</script> </script>
<script lang="ts"> <script lang="ts">

View file

@ -18,22 +18,22 @@
<div class="flex flex-row border border-white rounded-md overflow-hidden"> <div class="flex flex-row border border-white rounded-md overflow-hidden">
<EyeIcon <EyeIcon
class="w-5 h-5 p-1 box-content cursor-pointer" class="w-5 h-5 p-1 box-content cursor-pointer"
:class="_can(permissionUpdate, 'read', section) ? 'bg-success' : ''" :class="_canSection(permissionUpdate, 'read', section) ? 'bg-success' : ''"
@click="togglePermission('read', section)" @click="togglePermission('read', section)"
/> />
<PlusIcon <PlusIcon
class="w-5 h-5 p-1 box-content cursor-pointer" class="w-5 h-5 p-1 box-content cursor-pointer"
:class="_can(permissionUpdate, 'create', section) ? 'bg-success' : ''" :class="_canSection(permissionUpdate, 'create', section) ? 'bg-success' : ''"
@click="togglePermission('create', section)" @click="togglePermission('create', section)"
/> />
<PencilIcon <PencilIcon
class="w-5 h-5 p-1 box-content cursor-pointer" class="w-5 h-5 p-1 box-content cursor-pointer"
:class="_can(permissionUpdate, 'update', section) ? 'bg-success' : ''" :class="_canSection(permissionUpdate, 'update', section) ? 'bg-success' : ''"
@click="togglePermission('update', section)" @click="togglePermission('update', section)"
/> />
<TrashIcon <TrashIcon
class="w-5 h-5 p-1 box-content cursor-pointer" class="w-5 h-5 p-1 box-content cursor-pointer"
:class="_can(permissionUpdate, 'delete', section) ? 'bg-success' : ''" :class="_canSection(permissionUpdate, 'delete', section) ? 'bg-success' : ''"
@click="togglePermission('delete', section)" @click="togglePermission('delete', section)"
/> />
</div> </div>
@ -132,7 +132,7 @@ export default defineComponent({
}; };
}, },
computed: { computed: {
...mapState(useAbilityStore, ["_can"]), ...mapState(useAbilityStore, ["_can", "_canSection"]),
canSaveOrReset(): boolean { canSaveOrReset(): boolean {
return isEqual(this.permissions, this.permissionUpdate); return isEqual(this.permissions, this.permissionUpdate);
}, },

View file

@ -109,8 +109,8 @@ import { Listbox, ListboxButton, ListboxOptions, ListboxOption, ListboxLabel } f
import { CheckIcon, ChevronUpDownIcon } from "@heroicons/vue/20/solid"; import { CheckIcon, ChevronUpDownIcon } from "@heroicons/vue/20/solid";
import { useMemberStore } from "@/stores/admin/club/member/member"; import { useMemberStore } from "@/stores/admin/club/member/member";
import type { CreateMemberViewModel } from "@/viewmodels/admin/club/member/member.models"; import type { CreateMemberViewModel } from "@/viewmodels/admin/club/member/member.models";
import { useSalutationStore } from "../../../../stores/admin/configuration/salutation"; import { useSalutationStore } from "@/stores/admin/configuration/salutation";
import type { SalutationViewModel } from "../../../../viewmodels/admin/configuration/salutation.models"; import type { SalutationViewModel } from "@/viewmodels/admin/configuration/salutation.models";
import { InformationCircleIcon } from "@heroicons/vue/24/outline"; import { InformationCircleIcon } from "@heroicons/vue/24/outline";
</script> </script>

View file

@ -36,8 +36,8 @@ import SuccessCheckmark from "@/components/SuccessCheckmark.vue";
import FailureXMark from "@/components/FailureXMark.vue"; import FailureXMark from "@/components/FailureXMark.vue";
import { useProtocolStore } from "@/stores/admin/club/protocol/protocol"; import { useProtocolStore } from "@/stores/admin/club/protocol/protocol";
import type { CreateProtocolViewModel } from "@/viewmodels/admin/club/protocol/protocol.models"; import type { CreateProtocolViewModel } from "@/viewmodels/admin/club/protocol/protocol.models";
import { useNewsletterStore } from "../../../../stores/admin/club/newsletter/newsletter"; import { useNewsletterStore } from "@/stores/admin/club/newsletter/newsletter";
import type { CreateNewsletterViewModel } from "../../../../viewmodels/admin/club/newsletter/newsletter.models"; import type { CreateNewsletterViewModel } from "@/viewmodels/admin/club/newsletter/newsletter.models";
</script> </script>
<script lang="ts"> <script lang="ts">

View file

@ -20,7 +20,7 @@ import { mapState, mapActions } from "pinia";
import { ArchiveBoxArrowDownIcon, ArrowDownTrayIcon, BarsArrowUpIcon } from "@heroicons/vue/24/outline"; import { ArchiveBoxArrowDownIcon, ArrowDownTrayIcon, BarsArrowUpIcon } from "@heroicons/vue/24/outline";
import { useAbilityStore } from "@/stores/ability"; import { useAbilityStore } from "@/stores/ability";
import { useModalStore } from "@/stores/modal"; import { useModalStore } from "@/stores/modal";
import { useBackupStore } from "../../../../stores/admin/management/backup"; import { useBackupStore } from "@/stores/admin/management/backup";
</script> </script>
<script lang="ts"> <script lang="ts">

View file

@ -57,9 +57,9 @@ import Spinner from "@/components/Spinner.vue";
import SuccessCheckmark from "@/components/SuccessCheckmark.vue"; import SuccessCheckmark from "@/components/SuccessCheckmark.vue";
import FailureXMark from "@/components/FailureXMark.vue"; import FailureXMark from "@/components/FailureXMark.vue";
import { useBackupStore } from "@/stores/admin/management/backup"; import { useBackupStore } from "@/stores/admin/management/backup";
import type { BackupRestoreViewModel } from "../../../../viewmodels/admin/management/backup.models"; import type { BackupRestoreViewModel } from "@/viewmodels/admin/management/backup.models";
import { InformationCircleIcon } from "@heroicons/vue/24/outline"; import { InformationCircleIcon } from "@heroicons/vue/24/outline";
import { backupSections, type BackupSection } from "../../../../types/backupTypes"; import { backupSections, type BackupSection } from "@/types/backupTypes";
</script> </script>
<script lang="ts"> <script lang="ts">

View file

@ -39,7 +39,7 @@ import Spinner from "@/components/Spinner.vue";
import SuccessCheckmark from "@/components/SuccessCheckmark.vue"; import SuccessCheckmark from "@/components/SuccessCheckmark.vue";
import FailureXMark from "@/components/FailureXMark.vue"; import FailureXMark from "@/components/FailureXMark.vue";
import { useWebapiStore } from "@/stores/admin/management/webapi"; import { useWebapiStore } from "@/stores/admin/management/webapi";
import type { CreateWebapiViewModel } from "../../../../viewmodels/admin/management/webapi.models"; import type { CreateWebapiViewModel } from "@/viewmodels/admin/management/webapi.models";
</script> </script>
<script lang="ts"> <script lang="ts">

View file

@ -28,7 +28,7 @@ import { CheckIcon, ChevronUpDownIcon } from "@heroicons/vue/20/solid";
import TextCopy from "@/components/TextCopy.vue"; import TextCopy from "@/components/TextCopy.vue";
import { CalendarDaysIcon, InformationCircleIcon } from "@heroicons/vue/24/outline"; import { CalendarDaysIcon, InformationCircleIcon } from "@heroicons/vue/24/outline";
import { host } from "@/serverCom"; import { host } from "@/serverCom";
import { useWebapiStore } from "../../../../stores/admin/management/webapi"; import { useWebapiStore } from "@/stores/admin/management/webapi";
</script> </script>
<script lang="ts"> <script lang="ts">

View file

@ -34,7 +34,7 @@ import Spinner from "@/components/Spinner.vue";
import SuccessCheckmark from "@/components/SuccessCheckmark.vue"; import SuccessCheckmark from "@/components/SuccessCheckmark.vue";
import FailureXMark from "@/components/FailureXMark.vue"; import FailureXMark from "@/components/FailureXMark.vue";
import { mapActions } from "pinia"; import { mapActions } from "pinia";
import { useSetupStore } from "../../stores/setup"; import { useSetupStore } from "@/stores/setup";
</script> </script>
<script lang="ts"> <script lang="ts">

View file

@ -32,7 +32,7 @@ import Spinner from "@/components/Spinner.vue";
import SuccessCheckmark from "@/components/SuccessCheckmark.vue"; import SuccessCheckmark from "@/components/SuccessCheckmark.vue";
import FailureXMark from "@/components/FailureXMark.vue"; import FailureXMark from "@/components/FailureXMark.vue";
import { mapActions } from "pinia"; import { mapActions } from "pinia";
import { useSetupStore } from "../../stores/setup"; import { useSetupStore } from "@/stores/setup";
</script> </script>
<script lang="ts"> <script lang="ts">

View file

@ -63,7 +63,7 @@ import Spinner from "@/components/Spinner.vue";
import SuccessCheckmark from "@/components/SuccessCheckmark.vue"; import SuccessCheckmark from "@/components/SuccessCheckmark.vue";
import FailureXMark from "@/components/FailureXMark.vue"; import FailureXMark from "@/components/FailureXMark.vue";
import { mapActions } from "pinia"; import { mapActions } from "pinia";
import { useSetupStore } from "../../stores/setup"; import { useSetupStore } from "@/stores/setup";
</script> </script>
<script lang="ts"> <script lang="ts">

View file

@ -37,7 +37,7 @@ import Spinner from "@/components/Spinner.vue";
import SuccessCheckmark from "@/components/SuccessCheckmark.vue"; import SuccessCheckmark from "@/components/SuccessCheckmark.vue";
import FailureXMark from "@/components/FailureXMark.vue"; import FailureXMark from "@/components/FailureXMark.vue";
import { mapActions } from "pinia"; import { mapActions } from "pinia";
import { useSetupStore } from "../../stores/setup"; import { useSetupStore } from "@/stores/setup";
</script> </script>
<script lang="ts"> <script lang="ts">

View file

@ -57,7 +57,7 @@ import Spinner from "@/components/Spinner.vue";
import SuccessCheckmark from "@/components/SuccessCheckmark.vue"; import SuccessCheckmark from "@/components/SuccessCheckmark.vue";
import FailureXMark from "@/components/FailureXMark.vue"; import FailureXMark from "@/components/FailureXMark.vue";
import { mapActions } from "pinia"; import { mapActions } from "pinia";
import { useSetupStore } from "../../stores/setup"; import { useSetupStore } from "@/stores/setup";
</script> </script>
<script lang="ts"> <script lang="ts">

View file

@ -1,4 +1,4 @@
import { joinTableFormatter, type FieldType, type QueryResult } from "../types/dynamicQueries"; import { joinTableFormatter, type FieldType, type QueryResult } from "@/types/dynamicQueries";
export function joinTableName(name: string): string { export function joinTableName(name: string): string {
let normalized = joinTableFormatter[name]; let normalized = joinTableFormatter[name];

View file

@ -1,4 +1,4 @@
import { useBackupStore } from "../stores/admin/management/backup"; import { useBackupStore } from "@/stores/admin/management/backup";
export async function setBackupPage(to: any, from: any, next: any) { export async function setBackupPage(to: any, from: any, next: any) {
const backup = useBackupStore(); const backup = useBackupStore();

View file

@ -1,7 +1,7 @@
import { useNewsletterStore } from "@/stores/admin/club/newsletter/newsletter"; import { useNewsletterStore } from "@/stores/admin/club/newsletter/newsletter";
import { useNewsletterDatesStore } from "@/stores/admin/club/newsletter/newsletterDates"; import { useNewsletterDatesStore } from "@/stores/admin/club/newsletter/newsletterDates";
import { useNewsletterRecipientsStore } from "@/stores/admin/club/newsletter/newsletterRecipients"; import { useNewsletterRecipientsStore } from "@/stores/admin/club/newsletter/newsletterRecipients";
import { useNewsletterPrintoutStore } from "../stores/admin/club/newsletter/newsletterPrintout"; import { useNewsletterPrintoutStore } from "@/stores/admin/club/newsletter/newsletterPrintout";
export async function setNewsletterId(to: any, from: any, next: any) { export async function setNewsletterId(to: any, from: any, next: any) {
const newsletter = useNewsletterStore(); const newsletter = useNewsletterStore();

View file

@ -3,7 +3,7 @@ import { useProtocolAgendaStore } from "@/stores/admin/club/protocol/protocolAge
import { useProtocolDecisionStore } from "@/stores/admin/club/protocol/protocolDecision"; import { useProtocolDecisionStore } from "@/stores/admin/club/protocol/protocolDecision";
import { useProtocolPresenceStore } from "@/stores/admin/club/protocol/protocolPresence"; import { useProtocolPresenceStore } from "@/stores/admin/club/protocol/protocolPresence";
import { useProtocolVotingStore } from "@/stores/admin/club/protocol/protocolVoting"; import { useProtocolVotingStore } from "@/stores/admin/club/protocol/protocolVoting";
import { useProtocolPrintoutStore } from "../stores/admin/club/protocol/protocolPrintout"; import { useProtocolPrintoutStore } from "@/stores/admin/club/protocol/protocolPrintout";
export async function setProtocolId(to: any, from: any, next: any) { export async function setProtocolId(to: any, from: any, next: any) {
const protocol = useProtocolStore(); const protocol = useProtocolStore();

View file

@ -11,21 +11,18 @@ export const useAbilityStore = defineStore("ability", {
getters: { getters: {
can: can:
(state) => (state) =>
(type: PermissionType | "admin", section: PermissionSection, module?: PermissionModule): boolean => { (type: PermissionType | "admin", section: PermissionSection, module: PermissionModule): boolean => {
const permissions = state.permissions; const permissions = state.permissions;
if (state.isOwner) return true; if (state.isOwner) return true;
if (type == "admin") return permissions?.admin ?? false; if (type == "admin") return permissions?.admin ?? permissions?.adminByOwner ?? false;
if (permissions?.admin) return true; if (permissions?.admin || permissions?.adminByOwner) return true;
if ( if (
(!module &&
permissions[section] != undefined &&
(permissions[section]?.all == "*" || permissions[section]?.all?.includes(type))) ||
permissions[section]?.all == "*" || permissions[section]?.all == "*" ||
permissions[section]?.all?.includes(type) permissions[section]?.all?.includes(type) ||
permissions[section]?.[module] == "*" ||
permissions[section]?.[module]?.includes(type)
) )
return true; return true;
if (module && (permissions[section]?.[module] == "*" || permissions[section]?.[module]?.includes(type)))
return true;
return false; return false;
}, },
canSection: canSection:
@ -33,8 +30,8 @@ export const useAbilityStore = defineStore("ability", {
(type: PermissionType | "admin", section: PermissionSection): boolean => { (type: PermissionType | "admin", section: PermissionSection): boolean => {
const permissions = state.permissions; const permissions = state.permissions;
if (state.isOwner) return true; if (state.isOwner) return true;
if (type == "admin") return permissions?.admin ?? false; if (type == "admin") return permissions?.admin ?? permissions?.adminByOwner ?? false;
if (permissions?.admin) return true; if (permissions?.admin || permissions?.adminByOwner) return true;
if ( if (
permissions[section]?.all == "*" || permissions[section]?.all == "*" ||
permissions[section]?.all?.includes(type) || permissions[section]?.all?.includes(type) ||
@ -54,20 +51,31 @@ export const useAbilityStore = defineStore("ability", {
permissions: PermissionObject, permissions: PermissionObject,
type: PermissionType | "admin", type: PermissionType | "admin",
section: PermissionSection, section: PermissionSection,
module?: PermissionModule module: PermissionModule
): boolean => { ): boolean => {
// ignores ownership // ignores ownership
if (type == "admin") return permissions?.admin ?? false; if (type == "admin") return permissions?.admin ?? permissions?.adminByOwner ?? false;
if (permissions?.admin) return true; if (permissions?.admin || permissions?.adminByOwner) return true;
if ( if (
(!module &&
permissions[section] != undefined &&
(permissions[section]?.all == "*" || permissions[section]?.all?.includes(type))) ||
permissions[section]?.all == "*" || permissions[section]?.all == "*" ||
permissions[section]?.all?.includes(type) permissions[section]?.all?.includes(type) ||
permissions[section]?.[module] == "*" ||
permissions[section]?.[module]?.includes(type)
) )
return true; return true;
if (module && (permissions[section]?.[module] == "*" || permissions[section]?.[module]?.includes(type))) return false;
},
_canSection:
() =>
(permissions: PermissionObject, type: PermissionType | "admin", section: PermissionSection): boolean => {
// ignores ownership
if (type == "admin") return permissions?.admin ?? permissions?.adminByOwner ?? false;
if (permissions?.admin || permissions?.adminByOwner) return true;
if (
permissions[section]?.all == "*" ||
permissions[section]?.all?.includes(type) ||
permissions[section] != undefined
)
return true; return true;
return false; return false;
}, },

View file

@ -1,9 +1,8 @@
import { defineStore } from "pinia"; import { defineStore } from "pinia";
import { http, newEventSource, streamingFetch } from "@/serverCom"; import { http, streamingFetch } from "@/serverCom";
import { useNewsletterStore } from "./newsletter"; import { useNewsletterStore } from "./newsletter";
import type { AxiosResponse } from "axios"; import type { AxiosResponse } from "axios";
import type { EventSourcePolyfill } from "event-source-polyfill"; import { useNotificationStore, type NotificationType } from "@/stores/notification";
import { useNotificationStore, type NotificationType } from "../../../notification";
export const useNewsletterPrintoutStore = defineStore("newsletterPrintout", { export const useNewsletterPrintoutStore = defineStore("newsletterPrintout", {
state: () => { state: () => {

View file

@ -6,10 +6,10 @@ import type {
} from "@/viewmodels/admin/configuration/query.models"; } from "@/viewmodels/admin/configuration/query.models";
import { http } from "@/serverCom"; import { http } from "@/serverCom";
import type { AxiosResponse } from "axios"; import type { AxiosResponse } from "axios";
import { useQueryBuilderStore } from "../club/queryBuilder"; import { useQueryBuilderStore } from "@/stores/admin/club/queryBuilder";
import { useModalStore } from "../../modal"; import { useModalStore } from "@/stores/modal";
import { defineAsyncComponent, markRaw } from "vue"; import { defineAsyncComponent, markRaw } from "vue";
import { useAbilityStore } from "../../ability"; import { useAbilityStore } from "@/stores/ability";
export const useQueryStoreStore = defineStore("queryStore", { export const useQueryStoreStore = defineStore("queryStore", {
state: () => { state: () => {

View file

@ -1,7 +1,7 @@
import { defineStore } from "pinia"; import { defineStore } from "pinia";
import { http } from "@/serverCom"; import { http } from "@/serverCom";
import type { AxiosResponse, AxiosProgressEvent } from "axios"; import type { AxiosResponse, AxiosProgressEvent } from "axios";
import type { BackupRestoreViewModel } from "../../../viewmodels/admin/management/backup.models"; import type { BackupRestoreViewModel } from "@/viewmodels/admin/management/backup.models";
export const useBackupStore = defineStore("backup", { export const useBackupStore = defineStore("backup", {
state: () => { state: () => {

View file

@ -1,7 +1,7 @@
import { defineStore } from "pinia"; import { defineStore } from "pinia";
import { useAbilityStore } from "@/stores/ability"; import { useAbilityStore } from "@/stores/ability";
import router from "@/router"; import router from "@/router";
import type { PermissionSection } from "../../types/permissionTypes"; import type { PermissionSection } from "@/types/permissionTypes";
export type navigationModel = { export type navigationModel = {
[key in topLevelNavigationType]: navigationSplitModel; [key in topLevelNavigationType]: navigationSplitModel;

View file

@ -1,5 +1,5 @@
import { defineStore } from "pinia"; import { defineStore } from "pinia";
import { http } from "../serverCom"; import { http } from "@/serverCom";
export const useConfigurationStore = defineStore("configuration", { export const useConfigurationStore = defineStore("configuration", {
state: () => { state: () => {

View file

@ -1,5 +1,5 @@
import { defineStore } from "pinia"; import { defineStore } from "pinia";
import { http } from "../serverCom"; import { http } from "@/serverCom";
import type { AxiosResponse } from "axios"; import type { AxiosResponse } from "axios";
import { useConfigurationStore } from "./configuration"; import { useConfigurationStore } from "./configuration";

View file

@ -31,6 +31,7 @@ export type PermissionString =
| `${PermissionSection}.${PermissionModule}.*` // für alle Berechtigungen in einem Modul | `${PermissionSection}.${PermissionModule}.*` // für alle Berechtigungen in einem Modul
| `${PermissionSection}.${PermissionType}` // für spezifische Berechtigungen in einem Abschnitt | `${PermissionSection}.${PermissionType}` // für spezifische Berechtigungen in einem Abschnitt
| `${PermissionSection}.*` // für alle Berechtigungen in einem Abschnitt | `${PermissionSection}.*` // für alle Berechtigungen in einem Abschnitt
| `additional.${string}.${string}` // additional
| "*"; // für Admin | "*"; // für Admin
export type PermissionObject = { export type PermissionObject = {
@ -39,10 +40,20 @@ export type PermissionObject = {
} & { all?: Array<PermissionType> | "*" }; } & { all?: Array<PermissionType> | "*" };
} & { } & {
admin?: boolean; admin?: boolean;
adminByOwner?: boolean;
} & {
additional?: { [key: string]: string };
}; };
export type SectionsAndModulesObject = { export type SectionsAndModulesObject = {
[section in PermissionSection]: Array<PermissionModule>; [section in PermissionSection]: Array<PermissionModule>;
} & {
additional?: Array<{
key: string;
name: string;
type: "number" | "string";
emptyIfAdmin: boolean;
}>;
}; };
export const permissionSections: Array<PermissionSection> = ["club", "configuration", "management"]; export const permissionSections: Array<PermissionSection> = ["club", "configuration", "management"];
@ -87,4 +98,7 @@ export const sectionsAndModules: SectionsAndModulesObject = {
"newsletter_config", "newsletter_config",
], ],
management: ["user", "role", "webapi", "backup", "setting"], management: ["user", "role", "webapi", "backup", "setting"],
additional: [
//{ key: "val", name: "name", type: "number", emptyIfAdmin: true },
],
}; };

View file

@ -1,4 +1,4 @@
import type { CalendarTypeViewModel } from "../configuration/calendarType.models"; import type { CalendarTypeViewModel } from "@/viewmodels/admin/configuration/calendarType.models";
export interface CalendarViewModel { export interface CalendarViewModel {
id: string; id: string;

View file

@ -1,4 +1,4 @@
import type { CommunicationTypeViewModel } from "../../configuration/communicationType.models"; import type { CommunicationTypeViewModel } from "@/viewmodels/admin/configuration/communicationType.models";
export interface CommunicationViewModel { export interface CommunicationViewModel {
id: number; id: number;

View file

@ -1,6 +1,6 @@
import type { CommunicationViewModel } from "./communication.models"; import type { CommunicationViewModel } from "./communication.models";
import type { MembershipViewModel } from "./membership.models"; import type { MembershipViewModel } from "./membership.models";
import type { SalutationViewModel } from "../../configuration/salutation.models"; import type { SalutationViewModel } from "@/viewmodels/admin/configuration/salutation.models";
export interface MemberViewModel { export interface MemberViewModel {
id: string; id: string;

View file

@ -1,4 +1,4 @@
import type { CalendarViewModel } from "../calendar.models"; import type { CalendarViewModel } from "@/viewmodels/admin/club/calendar.models";
export interface NewsletterDatesViewModel { export interface NewsletterDatesViewModel {
newsletterId: number; newsletterId: number;

View file

@ -1,4 +1,4 @@
import type { MemberViewModel } from "../member/member.models"; import type { MemberViewModel } from "@/viewmodels/admin/club/member/member.models";
export interface NewsletterRecipientsViewModel { export interface NewsletterRecipientsViewModel {
newsletterId: number; newsletterId: number;

View file

@ -1,4 +1,4 @@
import type { BackupSection } from "../../../types/backupTypes"; import type { BackupSection } from "@/types/backupTypes";
export interface BackupRestoreViewModel { export interface BackupRestoreViewModel {
filename: string; filename: string;

View file

@ -81,7 +81,7 @@ import FormBottomBar from "@/components/FormBottomBar.vue";
import AppLogo from "@/components/AppLogo.vue"; import AppLogo from "@/components/AppLogo.vue";
import { mapState } from "pinia"; import { mapState } from "pinia";
import { useConfigurationStore } from "@/stores/configuration"; import { useConfigurationStore } from "@/stores/configuration";
import { hashString } from "../helpers/crypto"; import { hashString } from "@/helpers/crypto";
</script> </script>
<script lang="ts"> <script lang="ts">

View file

@ -54,10 +54,10 @@ import Spinner from "@/components/Spinner.vue";
import SuccessCheckmark from "@/components/SuccessCheckmark.vue"; import SuccessCheckmark from "@/components/SuccessCheckmark.vue";
import FailureXMark from "@/components/FailureXMark.vue"; import FailureXMark from "@/components/FailureXMark.vue";
import TextCopy from "@/components/TextCopy.vue"; import TextCopy from "@/components/TextCopy.vue";
import TotpCheckAndScan from "../../components/account/TotpCheckAndScan.vue"; import TotpCheckAndScan from "@/components/account/TotpCheckAndScan.vue";
import PasswordChange from "../../components/account/PasswordChange.vue"; import PasswordChange from "@/components/account/PasswordChange.vue";
import ChangeToPassword from "../../components/account/ChangeToPassword.vue"; import ChangeToPassword from "@/components/account/ChangeToPassword.vue";
import ChangeToTOTP from "../../components/account/ChangeToTOTP.vue"; import ChangeToTOTP from "@/components/account/ChangeToTOTP.vue";
</script> </script>
<script lang="ts"> <script lang="ts">

View file

@ -103,7 +103,7 @@ import { Listbox, ListboxButton, ListboxOptions, ListboxOption, ListboxLabel } f
import { CheckIcon, ChevronUpDownIcon } from "@heroicons/vue/20/solid"; import { CheckIcon, ChevronUpDownIcon } from "@heroicons/vue/20/solid";
import cloneDeep from "lodash.clonedeep"; import cloneDeep from "lodash.clonedeep";
import isEqual from "lodash.isequal"; import isEqual from "lodash.isequal";
import { useSalutationStore } from "../../../../stores/admin/configuration/salutation"; import { useSalutationStore } from "@/stores/admin/configuration/salutation";
</script> </script>
<script lang="ts"> <script lang="ts">

View file

@ -38,7 +38,7 @@ import Pagination from "@/components/Pagination.vue";
import { useAbilityStore } from "@/stores/ability"; import { useAbilityStore } from "@/stores/ability";
import { useNewsletterStore } from "@/stores/admin/club/newsletter/newsletter"; import { useNewsletterStore } from "@/stores/admin/club/newsletter/newsletter";
import type { NewsletterViewModel } from "@/viewmodels/admin/club/newsletter/newsletter.models"; import type { NewsletterViewModel } from "@/viewmodels/admin/club/newsletter/newsletter.models";
import NewsletterListItem from "../../../../components/admin/club/newsletter/NewsletterListItem.vue"; import NewsletterListItem from "@/components/admin/club/newsletter/NewsletterListItem.vue";
</script> </script>
<script lang="ts"> <script lang="ts">

View file

@ -81,7 +81,7 @@ import FailureXMark from "@/components/FailureXMark.vue";
import { ArrowDownTrayIcon, ViewfinderCircleIcon } from "@heroicons/vue/24/outline"; import { ArrowDownTrayIcon, ViewfinderCircleIcon } from "@heroicons/vue/24/outline";
import { useModalStore } from "@/stores/modal"; import { useModalStore } from "@/stores/modal";
import { useAbilityStore } from "@/stores/ability"; import { useAbilityStore } from "@/stores/ability";
import { useNewsletterPrintoutStore } from "../../../../stores/admin/club/newsletter/newsletterPrintout"; import { useNewsletterPrintoutStore } from "@/stores/admin/club/newsletter/newsletterPrintout";
</script> </script>
<script lang="ts"> <script lang="ts">

View file

@ -53,8 +53,8 @@ import { useNewsletterStore } from "@/stores/admin/club/newsletter/newsletter";
import { useModalStore } from "@/stores/modal"; import { useModalStore } from "@/stores/modal";
import NewsletterSyncing from "@/components/admin/club/newsletter/NewsletterSyncing.vue"; import NewsletterSyncing from "@/components/admin/club/newsletter/NewsletterSyncing.vue";
import { PrinterIcon } from "@heroicons/vue/24/outline"; import { PrinterIcon } from "@heroicons/vue/24/outline";
import { useNewsletterDatesStore } from "../../../../stores/admin/club/newsletter/newsletterDates"; import { useNewsletterDatesStore } from "@/stores/admin/club/newsletter/newsletterDates";
import { useNewsletterRecipientsStore } from "../../../../stores/admin/club/newsletter/newsletterRecipients"; import { useNewsletterRecipientsStore } from "@/stores/admin/club/newsletter/newsletterRecipients";
</script> </script>
<script lang="ts"> <script lang="ts">

View file

@ -32,7 +32,7 @@ import { useSetupStore } from "@/stores/setup";
import Club from "@/components/setup/Club.vue"; import Club from "@/components/setup/Club.vue";
import Images from "@/components/setup/Images.vue"; import Images from "@/components/setup/Images.vue";
import Finished from "@/components/setup/Finished.vue"; import Finished from "@/components/setup/Finished.vue";
import Mail from "../../components/setup/Mail.vue"; import Mail from "@/components/setup/Mail.vue";
</script> </script>
<script lang="ts"> <script lang="ts">