feat: perinco Pulse - Zeiterfassung fuer perinco

Erste Fassung, live unter https://pulse.perinco.de.

Frontend React 19 + TypeScript + Tailwind 4 (Vite), Backend Express mit
mysql2 auf MariaDB, Exporte als PDF (pdfkit), Excel (exceljs) und CSV.
Aufbau, Designsystem und die sieben Themes kommen aus dem perinco CRM.

Fachlich:
- Buchung mit Wochen-, Monats- und Jahresansicht, Zeiten als 1:23 oder 1,5
- Projekte mit n Budgettoepfen: Laufzeit, Volumen, Sperre bei Ablauf oder
  aufgebrauchtem Budget, Buchung nur auf den passenden Topf
- Kunden, Team mit Hierarchie, Reports im Harvest-Stil, Monatsexporte
- Rollen user/lead/management/admin, doppelt geprueft (Menue und Backend)
- SSO gegen Microsoft Entra ID, Uebernahme der AD-Benutzer per Drag & Drop

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
schakal00783
2026-09-17 22:52:20 +02:00
co-authored by Claude Opus 5
commit ef676a95f4
104 changed files with 21030 additions and 0 deletions
+44
View File
@@ -0,0 +1,44 @@
# perinco Pulse - Apache Vhost
# Ablage: /etc/apache2/sites-available/pulse.perinco.de.conf
# Aktivieren: a2ensite pulse.perinco.de && systemctl reload apache2
#
# Reihenfolge beim Aufsetzen:
# 1. DNS A-Record pulse.perinco.de -> 31.70.144.60
# 2. Diese Datei aktivieren (HTTP-Teil), Apache neu laden
# 3. certbot --apache -d pulse.perinco.de (legt den :443-Block selbst an)
<VirtualHost *:80>
ServerName pulse.perinco.de
DocumentRoot /var/www/pulse/dist
<Directory /var/www/pulse/dist>
Options -Indexes +FollowSymLinks
AllowOverride None
Require all granted
FallbackResource /index.html
</Directory>
# Gehashte Build-Dateien duerfen dauerhaft im Cache liegen, index.html nie.
<IfModule mod_expires.c>
ExpiresActive On
<FilesMatch "\.(js|css|woff2|png|jpg|svg|ico)$">
ExpiresDefault "access plus 1 year"
</FilesMatch>
</IfModule>
<FilesMatch "index\.html$">
Header set Cache-Control "no-cache, must-revalidate"
</FilesMatch>
Header always set X-Content-Type-Options "nosniff"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set X-Frame-Options "SAMEORIGIN"
# API lokal auf 127.0.0.1:5010, von aussen nur hierueber erreichbar.
ProxyPreserveHost On
ProxyPass /api http://127.0.0.1:5010/api
ProxyPassReverse /api http://127.0.0.1:5010/api
RequestHeader set X-Forwarded-Proto "https"
ErrorLog ${APACHE_LOG_DIR}/pulse_error.log
CustomLog ${APACHE_LOG_DIR}/pulse_access.log combined
</VirtualHost>