Commit graph

94 commits

Author SHA1 Message Date
bb4e0d2bbb test(calc): Story 5.3/5.4 — variants.js node-testbar + Regressionstests (Juni-Faelle); Report-Splice als Paste-Artefakt verifiziert (kein Code-Bug) 2026-07-07 23:21:59 +02:00
4f0f143d68 docs: Story 5.2 — .env.example, CHANGELOG, README + CLAUDE.md auf 3-Varianten + Mehrbenutzer aktualisiert 2026-07-07 23:18:55 +02:00
d18bd30adf test(auth): Story 5.1 — Audit-Coverage-Integrationstest (login_ok, PII-frei) 2026-07-07 23:16:04 +02:00
8d51b50f9a feat(frontend): Epic 4 — Login-Overlay, Bootstrap-Gate (offline≠401), Sync-Auth, Nutzerwechsel-Isolation, Admin-UI, Logout 2026-07-07 23:12:02 +02:00
66717014d0 harden(auth): cookie secure-by-default, sendMagicLink try/catch (Neutralität), :id-Validierung (qwen-Backend-Gate, verifiziert) 2026-07-07 23:07:32 +02:00
480a4c3b1c harden(auth): atomarer Token-Claim in consumeLoginToken (TOCTOU-frei, qwen-Gate) 2026-07-07 22:54:11 +02:00
facbb48bbf feat(auth): Stories 3.2/3.3 — Admin-Nutzerverwaltung (add/list/remove) + Last-Admin-Guard + CASCADE 2026-07-07 22:47:22 +02:00
846ca4f83a feat(auth): Story 3.1 — /api/state pro Nutzer hinter Auth (Anti-IDOR), documents (user_id,key), Startup seed+migrate 2026-07-07 22:46:25 +02:00
0b25c7b493 feat(auth): Stories 2.3-2.5 — Auth-Routen (neutral request, scanner-sicheres confirm, me/logout, Notzugang) + cookie-parser 2026-07-07 22:41:11 +02:00
b08988c2a5 feat(auth): Story 2.2 — Mailer (nodemailer + Konsolen-Fallback) 2026-07-07 22:37:02 +02:00
08c32bcf0f feat(auth): Story 2.1 — In-Memory Rate-Limiter (pro Key) 2026-07-07 22:36:10 +02:00
07f1bccff0 feat(auth): Story 1.5 — atomare, idempotente Migration Alt-Daten → Admin 2026-07-07 22:34:30 +02:00
c994dbf3b5 feat(auth): Story 1.4 — seedAdmin + Fail-Fast auf ADMIN_EMAIL 2026-07-07 22:33:14 +02:00
c1fc60cf02 feat(auth): Story 1.3 — Sessions (idle+absolut, gedrosselter last_seen, delete) 2026-07-07 22:30:54 +02:00
fb708912d7 feat(auth): Story 1.2 — normalizeEmail + Login-Token (SHA-256, einmalig, 30min) 2026-07-07 22:29:03 +02:00
b2dbd31b41 feat(auth): Story 1.1 — Auth-Schema (users/tokens/sessions/audit) + foreign_keys + audit-Helper
Fuegt users/login_tokens/sessions/audit_log Tabellen + FK-Indizes zu
server/db.js hinzu (foreign_keys=ON fuer CASCADE), plus server/audit.js
(append-only, keine PII). Bestehende documents/history-Tabellen und
exports unveraendert. TDD: server/auth-schema.test.js zuerst rot,
dann gruen; volle Suite (24 Tests) regressionsfrei.
2026-07-07 22:21:59 +02:00
94965c583f docs(bmad): Epics & Stories (Party-Mode-Fixes: Audit-Helper früh, 1.4 gesplittet) 2026-07-07 22:19:10 +02:00
1b48dd1247 docs(bmad): PRD final + Architektur (Opus+qwen validiert), Addendum-Nachtraege 2026-07-07 22:13:39 +02:00
3fdc1b1dd0 chore(bmad): BMAD v6.10 Setup (bmm, de) + PRD/Addendum/Projekt-Kontext (adversarial validiert, revidiert) 2026-07-07 21:58:55 +02:00
6d956c7672 docs: Design-Spec Mehrbenutzer/Magic-Link (Team-Release v1.0) 2026-07-07 20:49:56 +02:00
fc8d6dd065 fix(sw): skipWaiting + clients.claim -> neue Version aktiviert sofort (kein Stuck-auf-altem-Cache mehr); v6->v7 2026-07-07 19:29:32 +02:00
0001735207 fix(image-import): Zielmonat aus Live-Dropdown, Titel-Namen matchen, Importer-Instanziierung nach Sync-Boot
- Zielmonat/-jahr aus month-select/year-select statt veraltetem app.currentMonth (stand fix auf heute)
- normalizeName entfernt Anreden/Titel (Herr/Frau/Dr.) -> matcht titellose Mitarbeiter, kein Doppelanlegen
- Regression aus Sync-Boot behoben: app.js erzeugt imageImporter nach window.app (DOMContentLoaded-Listener lief zu frueh)
- totes app.currentMonth/currentYear entfernt; SW-Cache v5->v6
2026-07-07 19:23:45 +02:00
7d50e5d938 Merge remote-tracking branch 'github/main'
# Conflicts:
#	.github/ISSUE_TEMPLATE/config.yml
#	.gitignore
2026-07-07 17:18:04 +02:00
76569b94d6 Merge: Stufe 1 — Server-Persistenz (SQLite, Sync, Backup) 2026-07-07 17:08:14 +02:00
4f3539bb75 fix(sync): pending-Flag nur leeren wenn keine neuere Aenderung anliegt (Datenverlust-Race)
_flush() loeschte KEY_PENDING unconditionally nach erfolgreichem PUT. Lief
waehrend des PUT ein weiterer push() (neue Aenderung B), wurde deren pending-
Status vom ersten Flush geloescht, obwohl B noch nicht auf dem Server ist.
Schloss der Nutzer den Tab davor, sah der naechste boot() pending=false und
uebernahm den Server-Stand -> B ging verloren.

Fix: Generationszaehler _dirty. push() inkrementiert ihn, _flush() merkt sich
den Stand vor dem await und loescht pending nur, wenn kein neuerer push()
in der Zwischenzeit lief.

Neue Tests in sync.test.js decken Race (pending bleibt gesetzt) und
Normalfall (pending wird geleert) ab.
2026-07-07 16:18:45 +02:00
49a3803aa7 docs: Deployment mit persistentem Volume und Basic-Auth
Update-Prozedur in CLAUDE.md auf volume-aware docker run umgestellt
(-v dienstplan-data:/data, -e DATA_DIR=/data) und Dockerfile-Snippet
auf den neuen Express/SQLite-Server aktualisiert. Hinweis ergaenzt,
dass DB und Backups auf dem Volume liegen und die Domain hinter Caddy
Basic-Auth steht.

Zusaetzlich .dockerignore ergaenzt: node_modules aus dem Host-Kontext
wurde sonst per COPY . . ins Image kopiert und ueberschrieb das im
Image gebaute better-sqlite3-Binary (invalid ELF header beim Start).
Ohne diesen Fix startet der Container nicht.
2026-07-07 16:10:51 +02:00
67a3f4b546 feat(sync): Frontend pusht Aenderungen und laedt Server-Stand beim Start
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 15:59:48 +02:00
787bc1416b feat(sync): DataSync-Modul mit getesteter Reconcile-Logik 2026-07-07 15:54:32 +02:00
8459aaed1d chore: lokale data/ (Dev-DB + Backups) ignorieren 2026-07-07 15:52:49 +02:00
97e58903b8 feat(backend): automatisches taegliches SQLite-Online-Backup 2026-07-07 15:50:34 +02:00
45503f03ee feat(backend): GET/PUT /api/state mit Dokument-Persistenz 2026-07-07 15:46:48 +02:00
9714d845bb feat(backend): SQLite-Schicht mit WAL, Dokumenten und Historie 2026-07-07 15:43:30 +02:00
3b0632c4b9 feat(backend): Express-Server liefert statisch + /api/health 2026-07-07 15:40:06 +02:00
4eed9d0c4c refactor(variants): extract shared ineligible/pay/deduct helpers
V1/V2/V3 duplicated the empty-result shape, the paid-shares/bonus math
and the Friday-priority deduction loop. Pull them into ineligibleResult,
payResult and deductInOrder; each variant keeps only its distinct
threshold, eligibility test and deduction slots.

paidShares is now computed uniformly as max(0, classified - deduction);
never-deducted slots carry deduction 0, so the result is byte-identical
(verified against a 13122-case golden master over the full input grid).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 11:47:57 +02:00
88cc0553f6 refactor(variants): drop unused isWinner flag on variant results
Each variant emitted isWinner:false and the calculator set winner.isWinner
=true, but the UI marks the winner via variantId===winnerId, never the
flag. Only one test read it. Removed from the 6 variant returns, the empty
result, and the calculator.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 11:45:15 +02:00
cb46e8fb04 refactor(calculator): drop unused dutyDetails/totalDuties result fields
No UI reads them - app.js computes its own labels via isQualifyingDay/
getDayTypeLabel when rendering the duty list. Only the test-suite asserted
them; those asserts are removed too. buildDutyDetails goes with them.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 11:43:09 +02:00
bbc8690a0b refactor(image-import): extract resolveEntryName, dedupe resolution
groupEntriesByResolvedEmployee and resolveImports carried an identical
resolvedNames -> ignore/new/assign branch. One helper returning
{resolved, isNew}; callers keep their own null/newEmployee handling.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 11:41:39 +02:00
de2befde7a refactor(app): extract getVacationMapForMonth, dedupe 4x vacation loop
The "loop employees -> getVacationMode -> calculateAllEmployees" block
was copy-pasted in calculateBonuses, generateEmailReport, exportCSV and
exportBonusReport. Collapse to one storage helper. Behaviour identical:
calculateAllEmployees only reads keys present in employeeDuties.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 11:40:06 +02:00
7efdcbe17b refactor(storage): drop unreachable try/catch in getDutiesForMonth
new Date() and the object spread never throw; the isNaN guard already
drops invalid dates via the .filter(). The inner catch could never fire.
Outer boundary try/catch is kept.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 11:38:12 +02:00
91565741a8 refactor(holidays): remove dead getHolidaysForYear
Zero callers across app, calculator, image-import and tests. Holiday
lookup goes through isHoliday/getHolidayName only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 11:37:36 +02:00
4d8437e508 docs: drop two more false-positive TODO entries
- Same-day duty overwrite: addDuty is keyed per employee+date, so a
  split Saturday duty between two employees creates two independent
  0.5 entries, not a conflict. Not a bug.
- Holiday cutoff 2030: not a current priority, dropped per user call.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-05 21:08:04 +02:00
9276e8c64d docs: correct TODO.md - tie-breaker was already fixed, not a bug
The eligible-over-ineligible tie-break in calculateMonthlyBonus was a
deliberate fix (commit c733c3e) covered by an explicit test
(test-suite.js:191-203). Flagging it as an open bug in TODO.md was a
misreading of calculator.js against the design spec's simplified
pseudocode; the spec's own test plan (section 12.5) and the actual
test suite confirm the current behavior is correct.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-05 20:23:13 +02:00
3598a4de9e docs: add TODO.md with known bonus-calculation gaps
Tie-breaker in calculateMonthlyBonus deviates from spec section 5,
same-day duties overwrite instead of summing, and holiday data only
covers 2025-2030.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-05 19:58:42 +02:00
722f961387 chore: cleanup dead code and duplicated logic from audit
- Remove broken webpack.yml (no package.json/webpack config exists)
- Drop dead RATE_NORMAL/RATE_WEEKEND fields on BonusCalculator; share the
  real rate constants across all three variants in variants.js
- Simplify getEmptyResult() to build allResults via .map instead of
  three manual spreads
- Replace 5 duplicated German month-name arrays and 2 weekday-name
  arrays in app.js with a shared monthName()/WEEKDAY_NAMES helper;
  drop the now-redundant getMonthName() method
- Reuse existing .modal/.modal-backdrop/.modal-content CSS classes in
  generateEmailReport() instead of hand-rolled inline styles
- Delegate ImageImporter.classify() to the shared classify() in
  variants.js instead of a duplicated copy
- Unify ad hoc YYYY-MM-DD formatting in app.js to use the existing
  HolidayProvider.formatDate() helper

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-05 19:23:14 +02:00
a6be57e964 chore: remove enterprise standards adoption boilerplate
Multi-tenant deploy policies, container/Quadlet hardening rules, and
ADR/SOP templates don't fit a single-maintainer static-site app with
one Dockerfile and no container orchestration.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-05 19:11:33 +02:00
5d8f7759d9 Merge pull request 'chore: adopt engineering standard v1.0.0' (#1) from chore/adopt-standards-v1.0.0 into main
Some checks failed
NodeJS with Webpack / build (22.x) (push) Has been cancelled
ci / standards-gate (push) Has been cancelled
NodeJS with Webpack / build (18.x) (push) Has been cancelled
NodeJS with Webpack / build (20.x) (push) Has been cancelled
2026-07-05 18:49:09 +02:00
Kenearos
cd6d1f7b57
chore: adopt engineering standard v1.0.0 (#3)
Adopt the org engineering standard (its-consulting/standards @ v1.0.0).

Adds baseline governance/CI/policy files rendered from the standard's
templates and pins .standards-version=1.0.0. Vendored OPA/Rego policies
under .standards/policies/ so CI runs the gate locally (no cross-repo dep).
Placeholders ({{ORG}}/{{REPO}}/{{OWNER_HANDLE}}/{{MAINTAINER_EMAIL}}) filled in.
Existing files that differ were left untouched by the adopter.

Automated rollout. Files created: 16.
2026-07-02 01:19:33 +02:00
9c0dc7b4e1 chore: adopt engineering standard v1.0.0
Some checks failed
ci / standards-gate (pull_request) Has been cancelled
NodeJS with Webpack / build (18.x) (pull_request) Has been cancelled
NodeJS with Webpack / build (20.x) (pull_request) Has been cancelled
NodeJS with Webpack / build (22.x) (pull_request) Has been cancelled
Adopt the org engineering standard (its-consulting/standards @ v1.0.0).

Adds baseline governance/CI/policy files rendered from the standard's
templates and pins .standards-version=1.0.0. Vendored OPA/Rego policies
under .standards/policies/ so CI runs the gate locally (no cross-repo dep).
Placeholders ({{ORG}}/{{REPO}}/{{OWNER_HANDLE}}/{{MAINTAINER_EMAIL}}) filled in.
Existing files that differ were left untouched by the adopter.

Automated rollout. Files created: 16.
2026-07-01 19:32:52 +02:00
262be954bb chore: remove implementation plans for shipped features
The bild-import and bonus-varianten plans were step-by-step build guides
consumed during implementation. Both features are merged and live.
Design specs in docs/specs/ stay for reference.
2026-05-12 19:36:22 +02:00
a92bf00d38 docs: replace Railway deployment section with Hetzner setup
Live URL, server details, container/network setup, Caddy block, and
update procedure for the self-hosted deployment.
2026-05-12 19:16:05 +02:00