Compare commits
18 Commits
d450175871
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 742687f82b | |||
|
|
fddde56076 | ||
|
|
c385df4a0c | ||
| 7152b54b68 | |||
|
|
e44d3c5c74 | ||
|
|
c409fa6d4b | ||
| 21b4e8bc9f | |||
|
|
0fa19493c5 | ||
|
|
c145a723ed | ||
| 91fb3d79c5 | |||
|
|
d0b15f3e83 | ||
|
|
e60c0f4481 | ||
|
|
27a096546f | ||
| aee8705fa3 | |||
|
|
20a1cdd7f2 | ||
|
|
e3825ad217 | ||
| 99c03dccf2 | |||
|
|
a12f1f7815 |
@@ -8,6 +8,7 @@ on:
|
||||
jobs:
|
||||
analyze:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'pull_request'
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
@@ -31,10 +32,6 @@ jobs:
|
||||
node -v
|
||||
npm -v
|
||||
|
||||
- name: Require package version change for main PRs
|
||||
if: github.event_name == 'pull_request' && github.base_ref == 'main'
|
||||
run: scripts/check-package-version-changed.sh origin/main
|
||||
|
||||
- name: gitleaks (Secrets Scanning)
|
||||
run: |
|
||||
# Try to get the latest release asset URL
|
||||
@@ -95,7 +92,6 @@ jobs:
|
||||
|
||||
deploy-production:
|
||||
runs-on: ubuntu-latest
|
||||
needs: analyze
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||
steps:
|
||||
- name: Prepare SSH
|
||||
@@ -126,7 +122,6 @@ jobs:
|
||||
|
||||
deploy-test:
|
||||
runs-on: ubuntu-latest
|
||||
needs: analyze
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/dev'
|
||||
steps:
|
||||
- name: Prepare SSH
|
||||
|
||||
20
.gitea/workflows/version-gate.yml
Normal file
20
.gitea/workflows/version-gate.yml
Normal file
@@ -0,0 +1,20 @@
|
||||
name: Require Package Version Change
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
|
||||
jobs:
|
||||
check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
|
||||
- name: Check package.json version changed
|
||||
run: scripts/check-package-version-changed.sh origin/main
|
||||
@@ -64,6 +64,37 @@ install_dependencies() {
|
||||
fi
|
||||
}
|
||||
|
||||
install_dependencies_if_needed() {
|
||||
local cache_dir=".deploy-cache"
|
||||
local lock_hash_file="$cache_dir/package-lock.sha256"
|
||||
local current_lock_hash=""
|
||||
local previous_lock_hash=""
|
||||
|
||||
if [ ! -f "package-lock.json" ]; then
|
||||
echo " package-lock.json fehlt, führe npm install aus..."
|
||||
install_dependencies
|
||||
return 0
|
||||
fi
|
||||
|
||||
mkdir -p "$cache_dir"
|
||||
current_lock_hash="$(sha256sum package-lock.json | awk '{print $1}')"
|
||||
if [ -f "$lock_hash_file" ]; then
|
||||
previous_lock_hash="$(cat "$lock_hash_file" 2>/dev/null || true)"
|
||||
fi
|
||||
|
||||
if [ ! -d "node_modules" ]; then
|
||||
echo " node_modules fehlt, installiere Dependencies..."
|
||||
install_dependencies
|
||||
elif [ "$current_lock_hash" != "$previous_lock_hash" ]; then
|
||||
echo " package-lock.json geändert, führe npm ci aus..."
|
||||
install_dependencies
|
||||
else
|
||||
echo " package-lock.json unverändert, überspringe npm ci"
|
||||
fi
|
||||
|
||||
printf '%s\n' "$current_lock_hash" > "$lock_hash_file"
|
||||
}
|
||||
|
||||
use_project_node() {
|
||||
export NVM_DIR="${NVM_DIR:-$HOME/.nvm}"
|
||||
if [ -s "$NVM_DIR/nvm.sh" ]; then
|
||||
@@ -239,7 +270,7 @@ echo ""
|
||||
echo "3. Installing dependencies..."
|
||||
use_project_node
|
||||
ensure_node_version
|
||||
install_dependencies
|
||||
install_dependencies_if_needed
|
||||
|
||||
# 4. Remove old build (but keep data!)
|
||||
echo ""
|
||||
@@ -262,11 +293,16 @@ if [ -d ".output" ]; then
|
||||
echo " ✓ .output gelöscht"
|
||||
fi
|
||||
|
||||
# Auch .nuxt Cache löschen für sauberen Build
|
||||
if [ -d ".nuxt" ]; then
|
||||
echo " Removing .nuxt cache..."
|
||||
# .nuxt standardmäßig behalten (beschleunigt Folge-Builds deutlich).
|
||||
# Für erzwungenen Clean-Build: CLEAN_NUXT_CACHE=1 ./deploy-production.sh
|
||||
if [ "${CLEAN_NUXT_CACHE:-0}" = "1" ]; then
|
||||
if [ -d ".nuxt" ]; then
|
||||
echo " CLEAN_NUXT_CACHE=1 gesetzt: entferne .nuxt cache..."
|
||||
rm -rf .nuxt
|
||||
echo " ✓ .nuxt gelöscht"
|
||||
fi
|
||||
else
|
||||
echo " Behalte .nuxt cache für schnelleren Build (CLEAN_NUXT_CACHE=1 für Clean-Build)"
|
||||
fi
|
||||
|
||||
# Prüfe, ob node_modules vorhanden ist (für npm run build)
|
||||
@@ -289,17 +325,20 @@ if [ ! -f "node_modules/.package-lock.json" ] && [ ! -f "package-lock.json" ]; t
|
||||
install_dependencies
|
||||
fi
|
||||
|
||||
# Build mit expliziter Fehlerbehandlung und Output-Capture
|
||||
BUILD_OUTPUT=$(npm run build 2>&1)
|
||||
BUILD_EXIT_CODE=$?
|
||||
|
||||
# Zeige Build-Output
|
||||
echo "$BUILD_OUTPUT"
|
||||
# Build mit expliziter Fehlerbehandlung und gleichzeitiger Log-Datei
|
||||
BUILD_LOG_FILE=".deploy-cache/build-$(date +%Y%m%d-%H%M%S).log"
|
||||
mkdir -p ".deploy-cache"
|
||||
if npm run build 2>&1 | tee "$BUILD_LOG_FILE"; then
|
||||
BUILD_EXIT_CODE=0
|
||||
else
|
||||
BUILD_EXIT_CODE=$?
|
||||
fi
|
||||
|
||||
if [ "$BUILD_EXIT_CODE" -ne 0 ]; then
|
||||
echo ""
|
||||
echo "ERROR: Build fehlgeschlagen mit Exit-Code $BUILD_EXIT_CODE"
|
||||
echo "Bitte prüfen Sie die Build-Ausgabe oben auf Fehler."
|
||||
echo "Build-Log: $BUILD_LOG_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -308,10 +347,11 @@ echo " Synchronizing public documents into build output..."
|
||||
sync_public_documents_to_build
|
||||
|
||||
# Prüfe auf Warnungen im Build-Output, die auf Probleme hinweisen
|
||||
if echo "$BUILD_OUTPUT" | grep -qi "error\|failed\|missing"; then
|
||||
if rg -i "error|failed|missing" "$BUILD_LOG_FILE" >/dev/null 2>&1; then
|
||||
echo ""
|
||||
echo "WARNING: Build-Output enthält möglicherweise Fehler oder Warnungen."
|
||||
echo "Bitte prüfen Sie die Ausgabe oben."
|
||||
echo "Build-Log: $BUILD_LOG_FILE"
|
||||
fi
|
||||
|
||||
# Prüfe, ob der Build erfolgreich war - mehrere Checks
|
||||
|
||||
@@ -77,6 +77,37 @@ install_dependencies() {
|
||||
fi
|
||||
}
|
||||
|
||||
install_dependencies_if_needed() {
|
||||
local cache_dir=".deploy-cache"
|
||||
local lock_hash_file="$cache_dir/package-lock.sha256"
|
||||
local current_lock_hash=""
|
||||
local previous_lock_hash=""
|
||||
|
||||
if [ ! -f "package-lock.json" ]; then
|
||||
echo " package-lock.json fehlt, führe npm install aus..."
|
||||
install_dependencies
|
||||
return 0
|
||||
fi
|
||||
|
||||
mkdir -p "$cache_dir"
|
||||
current_lock_hash="$(sha256sum package-lock.json | awk '{print $1}')"
|
||||
if [ -f "$lock_hash_file" ]; then
|
||||
previous_lock_hash="$(cat "$lock_hash_file" 2>/dev/null || true)"
|
||||
fi
|
||||
|
||||
if [ ! -d "node_modules" ]; then
|
||||
echo " node_modules fehlt, installiere Dependencies..."
|
||||
install_dependencies
|
||||
elif [ "$current_lock_hash" != "$previous_lock_hash" ]; then
|
||||
echo " package-lock.json geändert, führe npm ci aus..."
|
||||
install_dependencies
|
||||
else
|
||||
echo " package-lock.json unverändert, überspringe npm ci"
|
||||
fi
|
||||
|
||||
printf '%s\n' "$current_lock_hash" > "$lock_hash_file"
|
||||
}
|
||||
|
||||
use_project_node() {
|
||||
export NVM_DIR="${NVM_DIR:-$HOME/.nvm}"
|
||||
if [ -s "$NVM_DIR/nvm.sh" ]; then
|
||||
@@ -245,7 +276,7 @@ echo ""
|
||||
echo "3. Installing dependencies..."
|
||||
use_project_node
|
||||
ensure_node_version
|
||||
install_dependencies
|
||||
install_dependencies_if_needed
|
||||
|
||||
# 4. Remove old build (but keep data!)
|
||||
echo ""
|
||||
@@ -268,11 +299,16 @@ if [ -d ".output" ]; then
|
||||
echo " ✓ .output gelöscht"
|
||||
fi
|
||||
|
||||
# Auch .nuxt Cache löschen für sauberen Build
|
||||
if [ -d ".nuxt" ]; then
|
||||
echo " Removing .nuxt cache..."
|
||||
# .nuxt standardmäßig behalten (beschleunigt Folge-Builds deutlich).
|
||||
# Für erzwungenen Clean-Build: CLEAN_NUXT_CACHE=1 ./deploy-test.sh
|
||||
if [ "${CLEAN_NUXT_CACHE:-0}" = "1" ]; then
|
||||
if [ -d ".nuxt" ]; then
|
||||
echo " CLEAN_NUXT_CACHE=1 gesetzt: entferne .nuxt cache..."
|
||||
rm -rf .nuxt
|
||||
echo " ✓ .nuxt gelöscht"
|
||||
fi
|
||||
else
|
||||
echo " Behalte .nuxt cache für schnelleren Build (CLEAN_NUXT_CACHE=1 für Clean-Build)"
|
||||
fi
|
||||
|
||||
# Prüfe, ob node_modules vorhanden ist (für npm run build)
|
||||
@@ -295,17 +331,20 @@ if [ ! -f "node_modules/.package-lock.json" ] && [ ! -f "package-lock.json" ]; t
|
||||
install_dependencies
|
||||
fi
|
||||
|
||||
# Build mit expliziter Fehlerbehandlung und Output-Capture
|
||||
BUILD_OUTPUT=$(npm run build 2>&1)
|
||||
BUILD_EXIT_CODE=$?
|
||||
|
||||
# Zeige Build-Output
|
||||
echo "$BUILD_OUTPUT"
|
||||
# Build mit expliziter Fehlerbehandlung und gleichzeitiger Log-Datei
|
||||
BUILD_LOG_FILE=".deploy-cache/build-$(date +%Y%m%d-%H%M%S).log"
|
||||
mkdir -p ".deploy-cache"
|
||||
if npm run build 2>&1 | tee "$BUILD_LOG_FILE"; then
|
||||
BUILD_EXIT_CODE=0
|
||||
else
|
||||
BUILD_EXIT_CODE=$?
|
||||
fi
|
||||
|
||||
if [ "$BUILD_EXIT_CODE" -ne 0 ]; then
|
||||
echo ""
|
||||
echo "ERROR: Build fehlgeschlagen mit Exit-Code $BUILD_EXIT_CODE"
|
||||
echo "Bitte prüfen Sie die Build-Ausgabe oben auf Fehler."
|
||||
echo "Build-Log: $BUILD_LOG_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -314,10 +353,11 @@ echo " Synchronizing public documents into build output..."
|
||||
sync_public_documents_to_build
|
||||
|
||||
# Prüfe auf Warnungen im Build-Output, die auf Probleme hinweisen
|
||||
if echo "$BUILD_OUTPUT" | grep -qi "error\|failed\|missing"; then
|
||||
if rg -i "error|failed|missing" "$BUILD_LOG_FILE" >/dev/null 2>&1; then
|
||||
echo ""
|
||||
echo "WARNING: Build-Output enthält möglicherweise Fehler oder Warnungen."
|
||||
echo "Bitte prüfen Sie die Ausgabe oben."
|
||||
echo "Build-Log: $BUILD_LOG_FILE"
|
||||
fi
|
||||
|
||||
# Prüfe, ob der Build erfolgreich war - mehrere Checks
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
// https://nuxt.com/docs/api/configuration/nuxt-config
|
||||
export default defineNuxtConfig({
|
||||
devtools: { enabled: true },
|
||||
devtools: { enabled: process.env.NODE_ENV !== 'production' },
|
||||
|
||||
modules: ['@nuxtjs/tailwindcss', '@pinia/nuxt'],
|
||||
|
||||
nitro: {
|
||||
preset: 'node-server',
|
||||
dev: process.env.NODE_ENV !== 'production'
|
||||
dev: process.env.NODE_ENV !== 'production',
|
||||
sourceMap: false
|
||||
},
|
||||
|
||||
vite: {
|
||||
build: {
|
||||
reportCompressedSize: false
|
||||
}
|
||||
},
|
||||
|
||||
// Erzwinge Dev-Port und Host zuverlässig für `npm run dev`
|
||||
|
||||
12
package-lock.json
generated
12
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "harheimertc-website",
|
||||
"version": "1.1.0",
|
||||
"version": "1.1.4",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "harheimertc-website",
|
||||
"version": "1.1.0",
|
||||
"version": "1.1.4",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@pinia/nuxt": "^0.11.2",
|
||||
@@ -36,7 +36,7 @@
|
||||
"eslint-plugin-vue": "^10.6.2",
|
||||
"globals": "^16.5.0",
|
||||
"lucide-vue-next": "^0.344.0",
|
||||
"postcss": "^8.4.0",
|
||||
"postcss": "^8.5.12",
|
||||
"supertest": "^7.1.0",
|
||||
"tailwindcss": "^3.4.0",
|
||||
"vitest": "^4.0.16",
|
||||
@@ -11366,9 +11366,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.5.6",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
|
||||
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
|
||||
"version": "8.5.12",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.12.tgz",
|
||||
"integrity": "sha512-W62t/Se6rA0Az3DfCL0AqJwXuKwBeYg6nOaIgzP+xZ7N5BFCI7DYi1qs6ygUYT6rvfi6t9k65UMLJC+PHZpDAA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "harheimertc-website",
|
||||
"version": "1.1.0",
|
||||
"version": "1.1.6",
|
||||
"description": "Moderne Webseite für den Harheimer Tischtennis Club",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
@@ -50,7 +50,7 @@
|
||||
"eslint-plugin-vue": "^10.6.2",
|
||||
"globals": "^16.5.0",
|
||||
"lucide-vue-next": "^0.344.0",
|
||||
"postcss": "^8.4.0",
|
||||
"postcss": "^8.5.12",
|
||||
"supertest": "^7.1.0",
|
||||
"tailwindcss": "^3.4.0",
|
||||
"vitest": "^4.0.16",
|
||||
|
||||
@@ -106,9 +106,31 @@
|
||||
|
||||
<!-- Active Users -->
|
||||
<div>
|
||||
<h2 class="text-2xl font-display font-bold text-gray-900 mb-4">
|
||||
Aktive Benutzer ({{ activeUsers.length }})
|
||||
<div class="flex flex-col gap-3 mb-4 sm:flex-row sm:items-end sm:justify-between">
|
||||
<h2 class="text-2xl font-display font-bold text-gray-900">
|
||||
Aktive Benutzer ({{ sortedActiveUsers.length }})
|
||||
</h2>
|
||||
<div class="flex items-center gap-2">
|
||||
<label
|
||||
for="user-sort-order"
|
||||
class="text-sm font-medium text-gray-700"
|
||||
>
|
||||
Sortierung
|
||||
</label>
|
||||
<select
|
||||
id="user-sort-order"
|
||||
v-model="nameSortMode"
|
||||
class="px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-primary-600"
|
||||
>
|
||||
<option value="firstLast">
|
||||
Vorname Nachname
|
||||
</option>
|
||||
<option value="lastFirst">
|
||||
Nachname, Vorname
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-white rounded-xl shadow-lg overflow-hidden">
|
||||
<table class="min-w-full divide-y divide-gray-200">
|
||||
<thead class="bg-gray-50">
|
||||
@@ -135,13 +157,13 @@
|
||||
</thead>
|
||||
<tbody class="bg-white divide-y divide-gray-200">
|
||||
<tr
|
||||
v-for="user in activeUsers"
|
||||
v-for="user in sortedActiveUsers"
|
||||
:key="user.id"
|
||||
class="hover:bg-gray-50"
|
||||
>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<div class="text-sm font-medium text-gray-900">
|
||||
{{ user.name }}
|
||||
{{ getDisplayName(user) }}
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
@@ -253,7 +275,7 @@
|
||||
>
|
||||
<div class="bg-white rounded-xl shadow-2xl max-w-md w-full p-6">
|
||||
<h2 class="text-2xl font-display font-bold text-gray-900 mb-4">
|
||||
Rollen bearbeiten: {{ editingUser.name }}
|
||||
Rollen bearbeiten: {{ getDisplayName(editingUser) }}
|
||||
</h2>
|
||||
|
||||
<div class="space-y-3 mb-6">
|
||||
@@ -350,6 +372,7 @@ const errorMessage = ref('')
|
||||
const showRoleModal = ref(false)
|
||||
const editingUser = ref(null)
|
||||
const selectedRoles = ref([])
|
||||
const nameSortMode = ref('firstLast')
|
||||
|
||||
const pendingUsers = computed(() => {
|
||||
return allUsers.value
|
||||
@@ -364,6 +387,61 @@ const activeUsers = computed(() => {
|
||||
return allUsers.value.filter(u => u.active === true)
|
||||
})
|
||||
|
||||
const splitNameParts = (name = '') => {
|
||||
const trimmed = (name || '').trim()
|
||||
if (!trimmed) {
|
||||
return { firstName: '', lastName: '' }
|
||||
}
|
||||
|
||||
if (trimmed.includes(',')) {
|
||||
const [lastNameRaw, ...firstNameRaw] = trimmed.split(',')
|
||||
return {
|
||||
firstName: firstNameRaw.join(',').trim(),
|
||||
lastName: (lastNameRaw || '').trim()
|
||||
}
|
||||
}
|
||||
|
||||
const parts = trimmed.split(/\s+/).filter(Boolean)
|
||||
if (parts.length <= 1) {
|
||||
return { firstName: parts[0] || '', lastName: '' }
|
||||
}
|
||||
|
||||
return {
|
||||
firstName: parts[0],
|
||||
lastName: parts.slice(1).join(' ')
|
||||
}
|
||||
}
|
||||
|
||||
const getDisplayName = (user) => {
|
||||
const { firstName, lastName } = splitNameParts(user?.name || '')
|
||||
|
||||
if (nameSortMode.value === 'lastFirst') {
|
||||
if (!lastName) {
|
||||
return firstName
|
||||
}
|
||||
return `${lastName}, ${firstName}`.trim()
|
||||
}
|
||||
|
||||
return `${firstName} ${lastName}`.trim()
|
||||
}
|
||||
|
||||
const sortedActiveUsers = computed(() => {
|
||||
return [...activeUsers.value].sort((a, b) => {
|
||||
const nameA = splitNameParts(a.name)
|
||||
const nameB = splitNameParts(b.name)
|
||||
|
||||
if (nameSortMode.value === 'lastFirst') {
|
||||
const lastNameCompare = nameA.lastName.localeCompare(nameB.lastName, 'de', { sensitivity: 'base' })
|
||||
if (lastNameCompare !== 0) return lastNameCompare
|
||||
return nameA.firstName.localeCompare(nameB.firstName, 'de', { sensitivity: 'base' })
|
||||
}
|
||||
|
||||
const firstNameCompare = nameA.firstName.localeCompare(nameB.firstName, 'de', { sensitivity: 'base' })
|
||||
if (firstNameCompare !== 0) return firstNameCompare
|
||||
return nameA.lastName.localeCompare(nameB.lastName, 'de', { sensitivity: 'base' })
|
||||
})
|
||||
})
|
||||
|
||||
const formatDate = (dateString) => {
|
||||
return new Date(dateString).toLocaleString('de-DE', {
|
||||
year: 'numeric',
|
||||
|
||||
@@ -3,9 +3,24 @@ import path from 'path'
|
||||
import { getUserFromToken } from '../../utils/auth.js'
|
||||
|
||||
async function readPackageVersion() {
|
||||
const packageJsonPath = path.join(process.cwd(), 'package.json')
|
||||
const cwd = process.cwd()
|
||||
const candidatePaths = [
|
||||
path.join(cwd, 'package.json'),
|
||||
path.join(cwd, '../package.json')
|
||||
]
|
||||
|
||||
for (const packageJsonPath of candidatePaths) {
|
||||
try {
|
||||
const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'))
|
||||
return String(packageJson.version || '')
|
||||
if (packageJson?.version) {
|
||||
return String(packageJson.version)
|
||||
}
|
||||
} catch (_error) {
|
||||
// Try next candidate path (e.g. .output runtime)
|
||||
}
|
||||
}
|
||||
|
||||
return ''
|
||||
}
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
|
||||
@@ -15,8 +15,11 @@ export default defineEventHandler(async (event) => {
|
||||
const cwd = process.cwd()
|
||||
const filename = 'mannschaften.csv'
|
||||
|
||||
// Prefer server/data, then .output/public/data, then public/data
|
||||
// Prefer CMS write target first (server/data/public-data),
|
||||
// then legacy locations.
|
||||
const candidates = [
|
||||
path.join(cwd, 'server/data/public-data', filename),
|
||||
path.join(cwd, '../server/data/public-data', filename),
|
||||
path.join(cwd, '.output/server/data', filename),
|
||||
path.join(cwd, 'server/data', filename),
|
||||
path.join(cwd, '.output/public/data', filename),
|
||||
|
||||
@@ -236,6 +236,22 @@ export async function getRecipientsByGroup(targetGroup) {
|
||||
email: m.email,
|
||||
name: `${m.firstName || ''} ${m.lastName || ''}`.trim() || m.name || ''
|
||||
}))
|
||||
|
||||
// Zusätzlich aktive Trainer aus users.json anschreiben
|
||||
users
|
||||
.filter(u => {
|
||||
if (!u.active || !u.email || !u.email.trim()) return false
|
||||
const roles = Array.isArray(u.roles) ? u.roles : (u.role ? [u.role] : [])
|
||||
return roles.includes('trainer')
|
||||
})
|
||||
.forEach(u => {
|
||||
if (!recipients.find(r => r.email.toLowerCase().trim() === u.email.toLowerCase().trim())) {
|
||||
recipients.push({
|
||||
email: u.email.trim(),
|
||||
name: u.name || ''
|
||||
})
|
||||
}
|
||||
})
|
||||
break
|
||||
|
||||
case 'mannschaftsspieler':
|
||||
|
||||
Reference in New Issue
Block a user