clean docu
All checks were successful
Deploy / deploy (push) Successful in 54s

This commit is contained in:
2026-08-10 20:11:03 +02:00
parent 49b248a39f
commit bd758f8b34
11 changed files with 34 additions and 12 deletions

View File

@@ -1,6 +1,6 @@
# Internal Documentation
Updated: 2026-08-03
Updated: 2026-08-10
Canonical internal project documentation lives in this directory.
@@ -11,6 +11,8 @@ Canonical internal project documentation lives in this directory.
- `en/README.md`
- `en/PROJECT_CONTEXT.md`
- `en/PROJECT_STRUCTURE.md`
- `db/schema.sql`
- `archive/legacy-root-files/`
## Maintenance Rule
- German and English documentation must be maintained as fully parallel references.

View File

@@ -0,0 +1,119 @@
Papa-Kind-Treff Projektstruktur und Pflegehinweise
Stand: 2026-07-24
1. Zweck
- Plattform für Väter mit Fokus auf lokale Treffen, Community/Forum, Mitgliederbereich und spätere Liveschaltung.
- Sprache und Tonalität im Frontend: freundlich, direkt, nutzerzentriert.
2. Zentrale Einstiegspunkte
- `public/index.php`
Front-Controller, Routing auf `public/page/*`, Staging-Basic-Auth, Layout-Steuerung.
- `partials/structure/layout_start.php`
Globale Assets, Body-Datasets, Consent-UI, Navigation, globale Modale.
- `partials/structure/layout_end.php`
Footer und Footer-Assets.
3. Wichtige Verzeichnisse
- `public/page/`
Route-Dateien.
Wichtige Seiten:
- `index.php` Startseite
- `search.php` Suche
- `community.php` Community-Übersicht
- `community_thread.php` Thread-Ansicht
- `dashboard.php` Mitgliederbereich
- `community-admin.php` Moderation/Admin
- `impressum.php`, `datenschutz.php`, `ueber-uns.php`
- `api/location-preference.php` API zum Speichern der Standortpräferenz
- `partials/landing/main/`
Startseiten-Blöcke.
- `partials/landing/community/`
Community-Templates inkl. Sidebar-Navigation.
- `partials/landing/account/`
Dashboard, Community-Admin, Login/Register.
- `partials/structure/`
Layout, Navigation, Footer, Matomo-Konfiguration.
- `src/App/`
Geschäftslogik und Services.
4. Relevante App-Klassen
- `src/App/AccountPages.php`
Dashboard-/Profil-Logik, Event-Anlage, Community-Bewerbung, Migration.
- `src/App/Community.php`
Boards, Threads, Posts, Forenstruktur, Punkte.
- `src/App/CommunityAccess.php`
Rollen, Bewerbungen, Meldungen, Moderation, Restrictions.
- `src/App/CommunityMigration.php`
DB-Erweiterungen für Community-Funktionen.
- `src/App/ProfileSettings.php`
Persistente Profileinstellungen für Standortfreigabe und Avatar-Merkmale.
- `src/App/Avatar.php`
Avatar-Presets, Asset-Pfade und Rendering im modernen Portrait-Stil.
- `src/App/Search.php`
Eventsuche inkl. Distanzfilter.
5. Frontend-Assets
- `public/assets/js/app.js`
Globale UI-Logik, Slider, Geolocation, Consent-Manager, Matomo-Opt-in.
- `public/assets/css/app.css`
Globales UI-Styling inkl. Community, Footer, Consent.
- `public/assets/css/styles.css`
Basis-Styles.
6. Aktueller Funktionsstand
- Startseite mit Hero, Suche, neueste Treffen, Community-Vorschau, Mitgliederbereich.
- Hauptnavigation aktuell reduziert auf `Home`, `Suche`, `Community`.
- Für eingeloggte Nutzer gibt es rechts ein Profil-Dropdown mit Direktlinks zu `Profil`, `Kinder`, `Termine`, `Community`, `Einstellungen`, `Abmelden`.
- Kein eigener Top-Level-Punkt `Termine`; Event-Fokus liegt derzeit auf Startseite und Suche.
- Community mit Kategorien, Boards, Thread-Liste, Thread-Ansicht, Rollen-/Moderationslogik.
- Mitgliederbereich als Bereichslayout mit linker Navigation und separaten Seiten für Profil, Kinder, Termine, Community und Einstellungen.
- Profilbereich mit moderner Avatar-Preset-Auswahl auf Basis echter Portrait-Assets.
- Community-Admin-Bereich für Bewerbungen, Meldungen, Rollen und Migration.
- Impressum, Datenschutz-&-Cookies-Seite und Über-uns vorhanden.
- Standortsortierung auf der Startseite für Treffen in der Nähe.
- Standortpräferenz im Einstellungsbereich:
- `disabled`
- `prompt`
- `enabled`
- Im Einstellungsbereich stehen zusätzlich Browser-Hinweise zur Standortfreigabe sowie der Einstieg in die Consent-Verwaltung.
- Consent-Manager:
- notwendige Cookies immer aktiv
- `analytics` für Matomo
- `external_services` für Standort/Karten/Leaflet/Nominatim
7. Rollenmodell Community
- normaler Nutzer
- schreiben, antworten, bewerten, melden
- `forum_admin`
- Themen/Beiträge bearbeiten und löschen
- Meldungen prüfen
- Community-Schreibsperren setzen
- `site_admin`
- alles wie `forum_admin`
- Bewerbungen prüfen
- Community-Migration
- `owner`
- alles wie `site_admin`
- Rollen vergeben/entziehen
8. Externe Dienste und rechtlich relevante Punkte
- Matomo (`matomo.my-statistics.info`) nur nach Consent `analytics`.
- Leaflet via `unpkg.com` nur nach Consent `external_services`.
- OpenStreetMap Nominatim für Geocoding/Reverse-Geocoding nur nach Consent `external_services` bei Browserfunktionen.
- Browser-Geolocation und lokale Standortspeicherung nur nach Consent `external_services` und zusätzlicher Standortpräferenz.
- Jede neue Einbindung von Cookies, LocalStorage, SessionStorage, Tracking, externen Skripten, APIs oder Drittanbietern muss:
- technisch im Consent-Manager berücksichtigt werden
- in `README.md` dokumentiert werden
- in dieser Datei vermerkt werden
- in den rechtlich notwendigen Hinweisen/Datenschutztexten ergänzt werden
9. Pflicht bei zukünftigen Änderungen
- Bei jeder funktionalen Änderung immer mitprüfen und bei Bedarf aktualisieren:
- `README.md`
- `.projectstructure.txt`
- `PROJECT_CONTEXT.md`
- Bei neuen Cookies, Tracking-Mechanismen oder Drittanbietern zusätzlich:
- Consent-Logik anpassen
- rechtliche Texte/Hinweise ergänzen
- Opt-in/Opt-out technisch verifizieren
- Änderungen an Navigation, Nutzerführung oder Rollenanzeige ebenfalls in dieser Datei, `README.md` und `PROJECT_CONTEXT.md` mitführen.

View File

@@ -0,0 +1,8 @@
Projektstruktur Verweis
Die kanonische interne Strukturdokumentation wurde verschoben nach:
- `Internal/de/PROJECT_STRUCTURE.md`
- `Internal/en/PROJECT_STRUCTURE.md`
Bitte diese Dateien als Referenz verwenden und bei Änderungen dort pflegen.

View File

@@ -0,0 +1,8 @@
# Projektkontext Verweis
Die kanonische interne Kontextdokumentation wurde verschoben nach:
- `Internal/de/PROJECT_CONTEXT.md`
- `Internal/en/PROJECT_CONTEXT.md`
Bitte diese Dateien als Referenz verwenden und bei Änderungen dort pflegen.

453
Internal/db/schema.sql Executable file
View File

@@ -0,0 +1,453 @@
-- Papa-Kind-Treff Basis-Schema (MySQL 8)
-- Hinweise:
-- - Passwörter mit Argon2id hashen.
-- - Sensible Felder werden app-seitig mit libsodium (XChaCha20-Poly1305) verschlüsselt
-- und als base64 in VARBINARY-Spalten abgelegt (nonce + cipher).
-- - Konto-E-Mails werden app-seitig verschlüsselt gespeichert; für die technische Suche
-- und Dublettenprüfung wird zusätzlich ein HMAC-SHA-256-Lookup-Hash abgelegt.
-- - share_level steuert Papa-Infos (basic, papa, papa_contact).
-- - children_visibility steuert Kinder-Infos separat (hidden, age_only, details).
CREATE TABLE users (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
email VARBINARY(512) NOT NULL,
email_lookup_hash CHAR(64) NOT NULL,
password_hash VARCHAR(255) NOT NULL,
status ENUM('active','pending','blocked') DEFAULT 'pending',
email_verified_at DATETIME NULL,
last_login_at DATETIME NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uq_users_email_lookup_hash (email_lookup_hash)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE user_profiles (
user_id BIGINT UNSIGNED PRIMARY KEY,
display_name VARCHAR(120) NOT NULL,
first_name VARBINARY(512) NULL,
last_name VARBINARY(512) NULL,
street VARBINARY(512) NULL,
share_level ENUM('basic','papa','papa_contact') NOT NULL DEFAULT 'basic',
children_visibility ENUM('hidden','age_only','details') NOT NULL DEFAULT 'hidden',
zip CHAR(5) NULL,
city VARCHAR(120) NULL,
region VARCHAR(120) NULL,
lat DECIMAL(10,7) NULL,
lng DECIMAL(10,7) NULL,
location_tracking_preference ENUM('disabled','prompt','enabled') NOT NULL DEFAULT 'prompt',
avatar_style VARCHAR(40) NOT NULL DEFAULT 'lorelei',
avatar_seed VARCHAR(100) NOT NULL DEFAULT '',
avatar_config_json TEXT NULL,
avatar_preset VARCHAR(64) NOT NULL DEFAULT 'papa-kind-treff',
avatar_lorelei_eyes_variant VARCHAR(24) NOT NULL DEFAULT '',
avatar_lorelei_eyebrows_variant VARCHAR(24) NOT NULL DEFAULT '',
avatar_lorelei_mouth_variant VARCHAR(24) NOT NULL DEFAULT '',
avatar_lorelei_glasses_variant VARCHAR(24) NOT NULL DEFAULT '',
avatar_lorelei_hair_variant VARCHAR(24) NOT NULL DEFAULT '',
avatar_lorelei_beard_variant VARCHAR(24) NOT NULL DEFAULT '',
avatar_lorelei_earrings_variant VARCHAR(24) NOT NULL DEFAULT '',
contact_phone VARBINARY(512) NULL,
contact_email VARBINARY(512) NULL,
profession VARBINARY(512) NULL,
languages VARBINARY(1024) NULL, -- JSON verschlüsselt
about VARBINARY(2048) NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT fk_profile_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
INDEX idx_profile_city (city),
INDEX idx_profile_region (region),
INDEX idx_profile_latlng (lat,lng)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE children (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT UNSIGNED NOT NULL,
gender ENUM('male','female','diverse','unknown') NOT NULL DEFAULT 'unknown',
birthdate DATE NULL,
age_years TINYINT UNSIGNED NULL,
encrypted_first_name VARBINARY(512) NOT NULL,
note VARBINARY(1024) NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT fk_child_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
INDEX idx_child_user (user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE events (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
created_by BIGINT UNSIGNED NOT NULL,
title VARCHAR(200) NOT NULL,
teaser_public VARCHAR(280) NOT NULL,
description TEXT NOT NULL,
category_slug VARCHAR(120) NULL,
image_path VARCHAR(255) NULL,
location_label VARCHAR(180) NULL,
street VARCHAR(180) NULL,
zip CHAR(5) NULL,
city VARCHAR(120) NULL,
region VARCHAR(120) NULL,
lat DECIMAL(10,7) NULL,
lng DECIMAL(10,7) NULL,
starts_at DATETIME NOT NULL,
ends_at DATETIME NULL,
max_participants SMALLINT UNSIGNED NULL,
allow_kids TINYINT(1) NOT NULL DEFAULT 1,
min_child_age TINYINT UNSIGNED NULL,
max_child_age TINYINT UNSIGNED NULL,
visibility ENUM('public','members') NOT NULL DEFAULT 'public',
status ENUM('draft','published','cancelled') NOT NULL DEFAULT 'published',
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT fk_event_user FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE CASCADE,
INDEX idx_event_city (city),
INDEX idx_event_region (region),
INDEX idx_event_time (starts_at),
INDEX idx_event_latlng (lat,lng)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE event_participants (
event_id BIGINT UNSIGNED NOT NULL,
user_id BIGINT UNSIGNED NOT NULL,
status ENUM('going','interested','waitlist','cancelled') NOT NULL DEFAULT 'going',
child_count TINYINT UNSIGNED NULL,
note VARBINARY(1024) NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (event_id, user_id),
CONSTRAINT fk_ep_event FOREIGN KEY (event_id) REFERENCES events(id) ON DELETE CASCADE,
CONSTRAINT fk_ep_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE system_settings (
`key` VARCHAR(120) NOT NULL PRIMARY KEY,
`value` TEXT NULL,
updated_by BIGINT UNSIGNED NULL,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT fk_system_settings_updated_by FOREIGN KEY (updated_by) REFERENCES users(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE listing_categories (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
slug VARCHAR(120) NOT NULL UNIQUE,
title VARCHAR(160) NOT NULL,
category_group ENUM('general','event','place','food','family','partner') NOT NULL DEFAULT 'general',
sort_order SMALLINT UNSIGNED NOT NULL DEFAULT 0,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE listing_places (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
created_by BIGINT UNSIGNED NULL,
source_type ENUM('user','partner','admin','system') NOT NULL DEFAULT 'user',
title VARCHAR(180) NOT NULL,
description TEXT NULL,
street VARCHAR(180) NULL,
zip CHAR(5) NULL,
city VARCHAR(120) NULL,
region VARCHAR(120) NULL,
lat DECIMAL(10,7) NULL,
lng DECIMAL(10,7) NULL,
website_url VARCHAR(255) NULL,
phone VARCHAR(60) NULL,
place_kind VARCHAR(80) NULL,
opening_hours_note TEXT NULL,
opening_hours_json LONGTEXT NULL,
provider_hint ENUM('manual','osm','google') NOT NULL DEFAULT 'manual',
external_place_id VARCHAR(190) NULL,
google_place_id VARCHAR(190) NULL,
rating_value DECIMAL(3,2) NULL,
rating_count INT UNSIGNED NULL,
status ENUM('draft','published','archived') NOT NULL DEFAULT 'published',
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT fk_listing_places_user FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE SET NULL,
INDEX idx_listing_places_city (city),
INDEX idx_listing_places_region (region),
INDEX idx_listing_places_latlng (lat,lng),
INDEX idx_listing_places_kind (place_kind)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE listings (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
created_by BIGINT UNSIGNED NULL,
owner_type ENUM('user','partner','admin','system') NOT NULL DEFAULT 'user',
listing_type ENUM('event','partner_offer','place','editorial_event') NOT NULL DEFAULT 'event',
primary_place_id BIGINT UNSIGNED NULL,
title VARCHAR(200) NOT NULL,
teaser_public VARCHAR(280) NOT NULL,
description TEXT NOT NULL,
image_path VARCHAR(255) NULL,
special_conditions_note TEXT NULL,
visibility ENUM('public','members') NOT NULL DEFAULT 'public',
status ENUM('draft','published','cancelled','archived') NOT NULL DEFAULT 'draft',
supports_registration TINYINT(1) NOT NULL DEFAULT 0,
supports_capacity TINYINT(1) NOT NULL DEFAULT 0,
supports_pricing TINYINT(1) NOT NULL DEFAULT 0,
is_recurring TINYINT(1) NOT NULL DEFAULT 0,
legacy_event_id BIGINT UNSIGNED NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT fk_listings_user FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE SET NULL,
CONSTRAINT fk_listings_place FOREIGN KEY (primary_place_id) REFERENCES listing_places(id) ON DELETE SET NULL,
CONSTRAINT fk_listings_legacy_event FOREIGN KEY (legacy_event_id) REFERENCES events(id) ON DELETE SET NULL,
INDEX idx_listings_type (listing_type),
INDEX idx_listings_status (status),
INDEX idx_listings_owner (owner_type)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE listing_category_map (
listing_id BIGINT UNSIGNED NOT NULL,
category_id BIGINT UNSIGNED NOT NULL,
PRIMARY KEY (listing_id, category_id),
CONSTRAINT fk_lcm_listing FOREIGN KEY (listing_id) REFERENCES listings(id) ON DELETE CASCADE,
CONSTRAINT fk_lcm_category FOREIGN KEY (category_id) REFERENCES listing_categories(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE listing_occurrences (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
listing_id BIGINT UNSIGNED NOT NULL,
occurrence_type ENUM('single','series','range','open_ended') NOT NULL DEFAULT 'single',
starts_at DATETIME NULL,
ends_at DATETIME NULL,
recurrence_rule VARCHAR(255) NULL,
recurrence_until DATETIME NULL,
capacity_total SMALLINT UNSIGNED NULL,
booking_url VARCHAR(255) NULL,
status ENUM('scheduled','cancelled','sold_out') NOT NULL DEFAULT 'scheduled',
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT fk_listing_occurrences_listing FOREIGN KEY (listing_id) REFERENCES listings(id) ON DELETE CASCADE,
INDEX idx_listing_occurrences_start (starts_at),
INDEX idx_listing_occurrences_type (occurrence_type),
INDEX idx_listing_occurrences_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE listing_prices (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
listing_id BIGINT UNSIGNED NOT NULL,
occurrence_id BIGINT UNSIGNED NULL,
label VARCHAR(120) NOT NULL,
audience ENUM('general','adult','child','family','group') NOT NULL DEFAULT 'general',
price_type ENUM('free','fixed','from','up_to','range','request') NOT NULL DEFAULT 'fixed',
amount DECIMAL(10,2) NULL,
amount_secondary DECIMAL(10,2) NULL,
currency CHAR(3) NOT NULL DEFAULT 'EUR',
note VARCHAR(255) NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT fk_listing_prices_listing FOREIGN KEY (listing_id) REFERENCES listings(id) ON DELETE CASCADE,
CONSTRAINT fk_listing_prices_occurrence FOREIGN KEY (occurrence_id) REFERENCES listing_occurrences(id) ON DELETE CASCADE,
INDEX idx_listing_prices_listing (listing_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE listing_benefits (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
listing_id BIGINT UNSIGNED NOT NULL,
occurrence_id BIGINT UNSIGNED NULL,
benefit_type ENUM('voucher_code','voucher_hint','discount_text') NOT NULL DEFAULT 'voucher_hint',
title VARCHAR(160) NOT NULL,
code VARCHAR(120) NULL,
description TEXT NULL,
valid_from DATETIME NULL,
valid_until DATETIME NULL,
is_public TINYINT(1) NOT NULL DEFAULT 1,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT fk_listing_benefits_listing FOREIGN KEY (listing_id) REFERENCES listings(id) ON DELETE CASCADE,
CONSTRAINT fk_listing_benefits_occurrence FOREIGN KEY (occurrence_id) REFERENCES listing_occurrences(id) ON DELETE CASCADE,
INDEX idx_listing_benefits_listing (listing_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Community / Forum
CREATE TABLE forum_categories (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
slug VARCHAR(120) NOT NULL UNIQUE,
title VARCHAR(180) NOT NULL,
sort_order SMALLINT UNSIGNED NOT NULL DEFAULT 0
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE forum_boards (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
category_id BIGINT UNSIGNED NOT NULL,
slug VARCHAR(120) NOT NULL UNIQUE,
title VARCHAR(180) NOT NULL,
description TEXT NULL,
icon VARCHAR(32) NULL,
sort_order SMALLINT UNSIGNED NOT NULL DEFAULT 0,
CONSTRAINT fk_fb_category FOREIGN KEY (category_id) REFERENCES forum_categories(id) ON DELETE CASCADE,
INDEX idx_fb_category (category_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE forum_threads (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
board_id BIGINT UNSIGNED NULL,
user_id BIGINT UNSIGNED NOT NULL,
title VARCHAR(200) NOT NULL,
body TEXT NOT NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT fk_ft_board FOREIGN KEY (board_id) REFERENCES forum_boards(id) ON DELETE SET NULL,
CONSTRAINT fk_ft_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
INDEX idx_ft_created (created_at),
INDEX idx_ft_board (board_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE forum_posts (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
thread_id BIGINT UNSIGNED NOT NULL,
user_id BIGINT UNSIGNED NOT NULL,
body TEXT NOT NULL,
highlighted_by BIGINT UNSIGNED NULL,
highlighted_at DATETIME NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT fk_fp_thread FOREIGN KEY (thread_id) REFERENCES forum_threads(id) ON DELETE CASCADE,
CONSTRAINT fk_fp_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
CONSTRAINT fk_fp_highlighted_by FOREIGN KEY (highlighted_by) REFERENCES users(id) ON DELETE SET NULL,
INDEX idx_fp_thread (thread_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE forum_post_feedback (
post_id BIGINT UNSIGNED NOT NULL,
user_id BIGINT UNSIGNED NOT NULL,
value TINYINT NOT NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (post_id, user_id),
CONSTRAINT fk_fpf_post FOREIGN KEY (post_id) REFERENCES forum_posts(id) ON DELETE CASCADE,
CONSTRAINT fk_fpf_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
INDEX idx_fpf_value (value)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE forum_reports (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
reporter_user_id BIGINT UNSIGNED NOT NULL,
target_type ENUM('thread','post') NOT NULL,
target_id BIGINT UNSIGNED NOT NULL,
reason TEXT NOT NULL,
status ENUM('open','resolved','dismissed') NOT NULL DEFAULT 'open',
moderator_user_id BIGINT UNSIGNED NULL,
moderator_note TEXT NULL,
resolved_at DATETIME NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT fk_fr_reporter FOREIGN KEY (reporter_user_id) REFERENCES users(id) ON DELETE CASCADE,
CONSTRAINT fk_fr_moderator FOREIGN KEY (moderator_user_id) REFERENCES users(id) ON DELETE SET NULL,
INDEX idx_fr_status (status),
INDEX idx_fr_target (target_type, target_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE user_roles (
user_id BIGINT UNSIGNED NOT NULL,
role ENUM('forum_admin','site_admin','owner') NOT NULL,
assigned_by BIGINT UNSIGNED NULL,
assigned_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (user_id, role),
CONSTRAINT fk_ur_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
CONSTRAINT fk_ur_assigned_by FOREIGN KEY (assigned_by) REFERENCES users(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE community_admin_applications (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT UNSIGNED NOT NULL,
motivation TEXT NOT NULL,
status ENUM('open','approved','rejected') NOT NULL DEFAULT 'open',
decision_reason TEXT NULL,
decided_by BIGINT UNSIGNED NULL,
decided_at DATETIME NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT fk_caa_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
CONSTRAINT fk_caa_decided_by FOREIGN KEY (decided_by) REFERENCES users(id) ON DELETE SET NULL,
INDEX idx_caa_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE community_user_restrictions (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT UNSIGNED NOT NULL,
restriction_type ENUM('thread_create_blocked','reply_blocked') NOT NULL,
reason TEXT NULL,
active TINYINT(1) NOT NULL DEFAULT 1,
created_by BIGINT UNSIGNED NOT NULL,
expires_at DATETIME NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT fk_cur_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
CONSTRAINT fk_cur_created_by FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE CASCADE,
INDEX idx_cur_user_active (user_id, active)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Punkte-Tracking (persistent, config-Änderungen wirken nur auf neue Einträge)
CREATE TABLE user_points (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT UNSIGNED NOT NULL,
action VARCHAR(64) NOT NULL,
amount DECIMAL(10,2) NOT NULL,
meta JSON NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT fk_up_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
INDEX idx_up_user_action (user_id, action),
INDEX idx_up_created (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE user_points_totals (
user_id BIGINT UNSIGNED PRIMARY KEY,
total DECIMAL(12,2) NOT NULL DEFAULT 0,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT fk_upt_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Session-Handling (neutral, keine sensiblen Inhalte)
CREATE TABLE sessions (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT UNSIGNED NOT NULL,
token_hash CHAR(64) NOT NULL UNIQUE, -- SHA-256 Hash des Session-Tokens
ip VARCHAR(45) NULL,
user_agent VARCHAR(255) NULL,
expires_at DATETIME NOT NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT fk_session_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
INDEX idx_session_expires (expires_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Kurzlebige Tokens (Passwort-Reset, Magic-Login, E-Mail-Verify)
CREATE TABLE user_tokens (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT UNSIGNED NOT NULL,
type ENUM('reset','verify','magic_login') NOT NULL,
code CHAR(12) NULL, -- z. B. 6-stelliger Code (optional Klartext)
token_hash CHAR(64) NOT NULL UNIQUE,
expires_at DATETIME NOT NULL,
used_at DATETIME NULL,
ip VARCHAR(45) NULL,
user_agent VARCHAR(255) NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT fk_ut_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
INDEX idx_ut_expires (expires_at),
INDEX idx_ut_type (type)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE user_calendar_feeds (
user_id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
token_encrypted TEXT NOT NULL,
token_lookup_hash CHAR(64) NOT NULL UNIQUE,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
rotated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
last_accessed_at DATETIME NULL,
CONSTRAINT fk_user_calendar_feed_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
INDEX idx_user_calendar_feeds_lookup (token_lookup_hash)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Audit-Log für wichtige Aktionen
CREATE TABLE audit_log (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT UNSIGNED NULL, -- NULL bei Gast-Ereignissen
action VARCHAR(100) NOT NULL, -- z.B. login, profile.update, event.create
target_type VARCHAR(50) NULL, -- z.B. user, event, child
target_id BIGINT UNSIGNED NULL,
metadata JSON NULL, -- nicht sensible Zusatzinfos
ip VARCHAR(45) NULL,
user_agent VARCHAR(255) NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
INDEX idx_audit_user (user_id),
INDEX idx_audit_action (action),
INDEX idx_audit_target (target_type, target_id),
INDEX idx_audit_created (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

View File

@@ -1,6 +1,6 @@
# Papa-Kind-Treff Projektstruktur
Stand: 2026-08-05
Stand: 2026-08-10
## Projektordner
- `config/`: Konfigurationen, inklusive `prod/` und `staging/`
@@ -10,6 +10,8 @@ Stand: 2026-08-05
- `debug/`: Debug-Hilfen und Logs
- `api/`: technische Endpunkte
- `Internal/`: interne Projektdokumentation in Deutsch und Englisch
- `Internal/db/`: internes Datenbankschema
- `Internal/archive/`: archivierte frühere interne Root-Dateien
## Aktuelle Strukturhinweise
- Avatar-Verwaltung: `src/App/Avatar/AvatarManager.php`
@@ -21,7 +23,7 @@ Stand: 2026-08-05
- Kalender-Export und abonnierbarer Feed: `src/App/CalendarSync.php` plus `public/page/calendar/export.php` und `public/page/calendar/feed.php`
- UI für getrennte Bereiche `Events` und `Ausflugsziele` im Mitgliederbereich: `partials/landing/account/dashboard.php`
- Preis-, Bild- und Ortseingabe für Ausflugsziele sowie reduzierte Event-Maske: `partials/landing/account/dashboard.php` plus Speicherung in `src/App/ListingCatalog.php`
- Legacy-Eigen-Events wurden erweitert in `src/App/AccountPages.php` und `schema.sql` um Kategorie- und Bildfelder
- Legacy-Eigen-Events wurden erweitert in `src/App/AccountPages.php` und `Internal/db/schema.sql` um Kategorie- und Bildfelder
- Kategorien-Prüfung und Zusammenführung für Seiten-Admins liegen ebenfalls in `partials/landing/account/dashboard.php` mit Logik in `src/App/ListingCatalog.php`
## Dokumentationsregel
@@ -34,3 +36,4 @@ Kanonische interne Dokumentation:
Zusätzlich:
- `README.md` im Hauptordner bleibt als Start-Doku für neue Chats bestehen.
- Änderungen an der internen Struktur müssen immer in Deutsch und Englisch parallel dokumentiert werden.
- Frühere interne Root-Dokumente liegen jetzt unter `Internal/archive/legacy-root-files/`.

View File

@@ -1,6 +1,6 @@
# Papa-Kind-Treff
Stand: 2026-08-05
Stand: 2026-08-10
Papa-Kind-Treff ist eine PHP-basierte Plattform für Väter mit Fokus auf lokale Events, Termine und Treffen, Community-Austausch und einen geschützten Mitgliederbereich.
@@ -39,7 +39,7 @@ Papa-Kind-Treff ist eine PHP-basierte Plattform für Väter mit Fokus auf lokale
- Templates: `partials/`
- App-Logik: `src/App/`
- Assets: `public/assets/`
- Datenbankschema: `schema.sql`
- Datenbankschema: `Internal/db/schema.sql`
- neue Basisklassen: `src/App/SystemSettings.php`, `src/App/ListingCatalog.php`
## Datenschutz und Sicherheit
@@ -61,3 +61,7 @@ Bei jeder Änderung im Projekt mitprüfen und bei Bedarf aktualisieren:
- Die Dokumentation in `Internal/de/` und `Internal/en/` ist inhaltlich vollständig parallel zu halten.
- Deutsch und Englisch sind beide verbindliche interne Referenzen.
- Die Root-`README.md` im Hauptordner bleibt der feste Einstieg für neue Chats und verweist auf diese Struktur.
## Root-Regel
- Im Root liegt für neue Chats inhaltlich nur noch die `README.md` als Einstieg.
- Frühere Root-Dokumente wurden nach `Internal/archive/legacy-root-files/` verschoben.

View File

@@ -1,6 +1,6 @@
# Papa-Kind-Treff Project Structure
Updated: 2026-08-05
Updated: 2026-08-10
## Main Directories
- `config/`: configuration, including `prod/` and `staging/`
@@ -10,6 +10,8 @@ Updated: 2026-08-05
- `debug/`: debugging helpers and logs
- `api/`: technical endpoints
- `Internal/`: internal documentation in German and English
- `Internal/db/`: internal database schema
- `Internal/archive/`: archived former internal root files
## Current Structure Notes
- avatar management: `src/App/Avatar/AvatarManager.php`
@@ -21,9 +23,10 @@ Updated: 2026-08-05
- calendar export and subscribable feed: `src/App/CalendarSync.php` plus `public/page/calendar/export.php` and `public/page/calendar/feed.php`
- separated `Events` and `Outings` UI in member area: `partials/landing/account/dashboard.php`
- pricing, image upload, and place/address input for outings plus a reduced own-event form: `partials/landing/account/dashboard.php` with persistence in `src/App/ListingCatalog.php`
- legacy own events were extended in `src/App/AccountPages.php` and `schema.sql` with category and image fields
- legacy own events were extended in `src/App/AccountPages.php` and `Internal/db/schema.sql` with category and image fields
## Documentation Rule
- canonical internal documentation lives in `Internal/de/` and `Internal/en/`
- `README.md` in the project root remains as the startup document for new chats
- structural changes must always be documented in German and English in parallel
- former internal root documentation files now live under `Internal/archive/legacy-root-files/`

View File

@@ -1,6 +1,6 @@
# Papa-Kind-Treff
Updated: 2026-08-05
Updated: 2026-08-10
Papa-Kind-Treff is a PHP-based platform for fathers focused on local events, appointments, and meetups, community exchange, and a protected member area.
@@ -38,7 +38,7 @@ Papa-Kind-Treff is a PHP-based platform for fathers focused on local events, app
- templates: `partials/`
- application logic: `src/App/`
- assets: `public/assets/`
- database schema: `schema.sql`
- database schema: `Internal/db/schema.sql`
- new base classes: `src/App/SystemSettings.php`, `src/App/ListingCatalog.php`
## Privacy and Security
@@ -60,3 +60,7 @@ Whenever project behavior or structure changes, review and update:
- Documentation in `Internal/de/` and `Internal/en/` must remain fully parallel in content.
- German and English are both binding internal references.
- The root `README.md` remains the fixed start document for new chats and must be reviewed when core project assumptions change.
## Root Rule
- In the root directory, only `README.md` remains as the content entry point for new chats.
- Former root documentation files were moved to `Internal/archive/legacy-root-files/`.