Skip to content
This repository was archived by the owner on Apr 6, 2026. It is now read-only.

feat(web): add legal pages (ToS, Privacy Policy, Impressum) with DSGVO compliance#333

Merged
karaktaka merged 12 commits into
mainfrom
feat/legal-pages
Mar 15, 2026
Merged

feat(web): add legal pages (ToS, Privacy Policy, Impressum) with DSGVO compliance#333
karaktaka merged 12 commits into
mainfrom
feat/legal-pages

Conversation

@karaktaka
Copy link
Copy Markdown
Contributor

@karaktaka karaktaka commented Mar 14, 2026

Summary

  • Add public /terms, /privacy, and /impressum routes required for Discord bot verification and German law compliance
  • Terms of Service and Privacy Policy in English and German with a language switcher on each page
  • Impressum with §5 DDG and §18 MStV required fields (name, address, contact, content responsibility)
  • Privacy Policy updated to DSGVO Art. 13 standards: data controller identity, legal basis per processing activity, full rights catalogue (Art. 15–21), supervisory authority (HmbBfDI), third-country transfer disclosure (Hetzner hosting, Blizzard/Riot APIs)
  • Terms of Service updated: advance-notice clause for changes, softened removal clause, governing law (Hamburg), ODR platform reference
  • Shared LegalPageLayout component with footerLinks array prop and optional showMeta
  • Legal links (ToS · Privacy · Impressum) added to login page footer

Test plan

  • Visit /terms, /privacy, /impressum without being logged in — all render correctly
  • Language switcher on each page switches between EN and DE content
  • Footer links on each legal page navigate to the other legal pages
  • Login page shows all three legal links
  • /impressum has no "Last updated" meta line
  • Build passes: npm run build --prefix web/frontend

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added Terms, Privacy Policy and Impressum pages with English and German translations.
    • New reusable legal page layout: gradient title, back link, language switcher, optional "last updated" meta, centered card and footer links.
    • Legal links now appear on the login screen.
    • Public display of legal contact/details on legal pages when enabled via site settings.

karaktaka and others added 3 commits March 14, 2026 19:16
Adds public /terms and /privacy routes with full legal content pages
matching the app's dark theme. Links appear in the login card footer
and in the sidebar footer (when expanded). Adds i18n keys for EN/DE.

Co-Authored-By: Claude <noreply@anthropic.com>
…move sidebar links

- Extract duplicated CSS/template from TermsView and PrivacyView into
  LegalPageLayout.vue component; each view now ~30 lines of content only
- Replace inline SVG back-arrows with <Icon icon="mdi:arrow-left" />
  consistent with the rest of the codebase
- Remove legal links from sidebar footer (poor fit); login page only

Co-Authored-By: Claude <noreply@anthropic.com>
- Add DE content to TermsView and PrivacyView via v-if/v-else on locale
- Add LanguageSwitcher to LegalPageLayout (top-right, same as login page)
- Move hardcoded "Last updated" date into i18n (legal.last_updated, EN/DE)
- Add position: relative to .legal-card to anchor the switcher

Co-Authored-By: Claude <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 14, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • ✅ Review completed - (🔄 Check again to review again)

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7f614dfa-2634-49aa-8a33-f94f321e0078

📥 Commits

Reviewing files that changed from the base of the PR and between f7d629e and 43a42c5.

📒 Files selected for processing (8)
  • docker-compose.test.yml
  • docker-compose.yml
  • web/config.py
  • web/frontend/src/composables/useLegalContact.ts
  • web/frontend/src/views/ImpressumView.vue
  • web/frontend/src/views/LoginView.vue
  • web/frontend/src/views/PrivacyView.vue
  • web/routes/legal.py

📝 Walkthrough

Walkthrough

Adds a LegalPageLayout component, three legal views, i18n translations for a new legal namespace (en/de), a frontend composable to fetch cached /api/legal/contact, backend endpoint and schema for legal contact, WebConfig legal fields and env mappings, router entries, and minor frontend formatting/import edits. (50 words)

Changes

Cohort / File(s) Summary
Frontend: Layout & Composable
web/frontend/src/components/LegalPageLayout.vue, web/frontend/src/composables/useLegalContact.ts
New reusable LegalPageLayout (props: title, backText, footerLinks, showMeta) with i18n and scoped styles; new composable useLegalContact() that lazily fetches and caches /api/legal/contact and exposes contact + isContactAvailable.
Frontend: Legal Views
web/frontend/src/views/TermsView.vue, web/frontend/src/views/PrivacyView.vue, web/frontend/src/views/ImpressumView.vue
Three new SFC views using LegalPageLayout and translations; Privacy includes structured sections and conditional contact/controller block; Impressum conditionally renders contact address/email.
Frontend: i18n
web/frontend/src/i18n/locales/en.ts, web/frontend/src/i18n/locales/de.ts
Added legal translation namespace with keys for terms, privacy, impressum, last_updated and nested page content for terms/privacy/impressum in both locales.
Frontend: Routing & Login UI
web/frontend/src/router/index.ts, web/frontend/src/views/LoginView.vue
Added routes /terms, /privacy, /impressum (lazy-loaded, meta.public = true); Login view now shows legal navigation links when contact.enabled is true.
Backend: API, Config & Schema
web/routes/legal.py, web/schemas.py, web/config.py, web/app.py
New GET /api/legal/contact router; added LegalContactResponse Pydantic model; extended WebConfig with legal_enabled and legal_* fields and env mapping; app registers the legal router.
Frontend: Misc formatting & imports
web/frontend/src/**/*
Numerous import/order/formatting tweaks across many frontend files; minor payload/assignment adjustment in one templates tab; mostly cosmetic.
Dev / Compose & tooling
docker-compose.test.yml, docker-compose.yml, web/frontend/package.json, biome.json, .pre-commit-config.yaml
Test compose: added NERPYBOT_WEB_LEGAL_* envs; main compose: commented example block; added biome config, npm scripts for lint/format, and pre-commit biome hook for frontend.
Docker / CI
docker-compose.test.yml
Test environment updated to include legal-related env vars for dashboard service.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Browser as Browser
    participant View as Legal View
    participant Composable as useLegalContact
    participant API as API /api/legal/contact
    participant Config as WebConfig

    rect rgba(200,200,255,0.5)
    Browser->>View: Navigate to /privacy (or /terms, /impressum)
    end

    rect rgba(200,255,200,0.5)
    View->>Composable: call useLegalContact()
    Composable->>API: GET /api/legal/contact
    end

    rect rgba(255,200,200,0.5)
    API->>Config: read WebConfig (dependency)
    Config-->>API: return legal_* values
    API-->>Composable: 200 {enabled,name,street,zip_city,country_*,email}
    Composable-->>View: provide reactive contact data
    View-->>Browser: render legal page with contact info and translations
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

review

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 70.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main feature: adding legal pages (Terms, Privacy, Impressum) with DSGVO compliance, which aligns with the substantial changes across backend config, API routes, frontend views, i18n, and composables.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/legal-pages
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/frontend/src/views/PrivacyView.vue`:
- Around line 14-166: The template duplicates the entire policy for German and
default locales (see the two <template v-if="locale.current === 'de'"> and
<template v-else> blocks in PrivacyView.vue), which risks drift; refactor by
extracting each static section and text into i18n keys (or a localized content
object) and render a single template that iterates/outputs those keys (keep
RouterLink usage intact), replacing hardcoded German/English strings with
$t('privacy.sectionX.title') / $t('privacy.sectionX.body') or a localized
sections array so both locales share the same structure and updates stay
synchronized.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: a6515c68-e8dc-4cdb-842c-baa7514a0d91

📥 Commits

Reviewing files that changed from the base of the PR and between 3107916 and 9d5fed7.

📒 Files selected for processing (9)
  • web/frontend/src/components/LegalPageLayout.vue
  • web/frontend/src/i18n/locales/de.ts
  • web/frontend/src/i18n/locales/en.ts
  • web/frontend/src/router/index.ts
  • web/frontend/src/views/ImpressumView.vue
  • web/frontend/src/views/LoginView.vue
  • web/frontend/src/views/PrivacyView.vue
  • web/frontend/src/views/TermsView.vue
  • web/frontend/src/views/guild/GuildDetailView.vue

Comment thread web/frontend/src/views/PrivacyView.vue Outdated
Add /impressum route with §5 DDG and §18 MStV required fields. Update
Privacy Policy with DSGVO Art. 13 mandatory disclosures (data controller
identity, legal basis, full rights catalogue, supervisory authority, third-
country transfers). Update Terms of Service with advance-notice clause for
changes, governing law/jurisdiction, and ODR platform reference. Add
Impressum link to login page and all legal page footers. Upgrade
LegalPageLayout to support multiple footer links and optional meta line.

Co-Authored-By: Claude <noreply@anthropic.com>

refactor(web): extract legal page content into i18n keys + runtime contact API

- Add legal contact fields to WebConfig (legal_name, legal_street,
  legal_zip_city, legal_country_en/de, legal_email) with NERPYBOT_WEB_LEGAL_*
  env var mappings
- Add LegalContactResponse schema and public GET /api/legal/contact endpoint
  (no auth required)
- Add useLegalContact composable — fetches contact data once, caches at
  module scope, leaves empty strings on failure
- Extract all page text into i18n keys under legal.impressum_page,
  legal.terms_page, legal.privacy_page namespaces (EN + DE)
- Refactor ImpressumView, TermsView, PrivacyView to single templates using
  t() + useLegalContact(); RouterLink-split keys for inline link paragraphs;
  v-html for bold-formatted list items

Co-Authored-By: Claude <noreply@anthropic.com>
karaktaka and others added 2 commits March 14, 2026 23:38
…se.yml

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/config.py`:
- Around line 98-103: The legal contact fields (legal_name, legal_street,
legal_zip_city, legal_country_en, legal_country_de, legal_email) are currently
loaded as optional and can be empty; update the config loading in web/config.py
to validate these values after _get is called and before returning/exposing the
config: check each of the listed variables for a non-empty string and, if any
are missing/empty, raise a clear configuration error (or log and abort startup)
so the application fails fast instead of serving blank /api/legal/contact
content. Ensure the validation references the exact variable names above so
maintainers can locate the checks easily.

In `@web/frontend/src/composables/useLegalContact.ts`:
- Around line 29-38: The bug sets the module-level flag fetched before the
network call completes, causing transient failures to be cached; change the
logic in useLegalContact (the fetched flag and the fetch("/api/legal/contact")
promise chain that assigns to contact) so that fetched is only set after a
successful response/JSON parse and Object.assign(contact, data) completes (or
set fetched in the success handler), and ensure the catch handler does not flip
fetched to true; this will let subsequent calls retry on earlier failures while
preserving the successful-case behavior.

In `@web/frontend/src/i18n/locales/de.ts`:
- Line 128: The long German legal string value ("Wir behalten uns das Recht vor,
NerpyBot ... Wir garantieren keine kontinuierliche, ununterbrochene
Verfügbarkeit des Dienstes.") exceeds the 120-character limit; split this string
into multiple shorter string literals concatenated (or use a template literal
without inserting an actual newline) so no source line exceeds 120 chars,
preserving spaces and punctuation and keeping the same key in the de.ts locale
object; apply the same wrapping approach to the other long entries mentioned
(the strings at lines 183 and 205) to comply with the project's line-length
rule.

In `@web/frontend/src/i18n/locales/en.ts`:
- Line 126: The long legal sentence in web/frontend/src/i18n/locales/en.ts ("We
reserve the right to remove NerpyBot from any server or revoke dashboard access,
particularly in cases of abuse or violation of these terms. Except in cases of
serious violations or abuse, we will provide reasonable advance notice. We do
not guarantee continuous, uninterrupted availability of the service.") exceeds
the 120-character line length rule; edit that string so no source line is longer
than 120 characters by breaking it into multiple shorter string literals
concatenated (or into a template literal kept within 120 chars per source line),
ensuring you update the exact string entry in en.ts and keep the original text
unchanged when concatenated.

In `@web/frontend/src/views/ImpressumView.vue`:
- Around line 20-23: The ImpressumView.vue template renders legal contact fields
unconditionally (using the contact object fields like contact.name,
contact.street, contact.zip_city, contact.country_de/country_en), which can
produce blank/missing mandatory details if /api/legal/contact is empty; update
the template to guard rendering with a top-level condition (e.g., check contact
is defined and has required properties) or render a clear fallback/placeholder
so the compliance block never shows empty lines, and apply the same guard to the
other similar block around the 29-37 region.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 824b6c60-1157-4831-9a5a-6b312066199d

📥 Commits

Reviewing files that changed from the base of the PR and between 9d5fed7 and ee281a4.

📒 Files selected for processing (13)
  • web/app.py
  • web/config.py
  • web/frontend/src/components/LegalPageLayout.vue
  • web/frontend/src/composables/useLegalContact.ts
  • web/frontend/src/i18n/locales/de.ts
  • web/frontend/src/i18n/locales/en.ts
  • web/frontend/src/router/index.ts
  • web/frontend/src/views/ImpressumView.vue
  • web/frontend/src/views/LoginView.vue
  • web/frontend/src/views/PrivacyView.vue
  • web/frontend/src/views/TermsView.vue
  • web/routes/legal.py
  • web/schemas.py

Comment thread web/config.py Outdated
Comment thread web/frontend/src/composables/useLegalContact.ts Outdated
Comment thread web/frontend/src/i18n/locales/de.ts
Comment thread web/frontend/src/i18n/locales/en.ts
Comment thread web/frontend/src/views/ImpressumView.vue Outdated
karaktaka and others added 3 commits March 15, 2026 00:28
- Add Biome 2.4.7 as frontend linter with recommended rules at repo root
  (biome.json); disable noUnusedVariables/noUnusedImports for Vue SFCs
  (Biome can't see template usage); disable CSS linter (Tailwind uses
  non-standard syntax)
- Apply Biome safe fixes across all frontend source files (import
  ordering, quote style, semicolons, line width)
- Add biome-check pre-commit hook alongside existing ruff and prettier
- Add `enabled` flag to LegalContactResponse schema and compute it from
  all 6 legal fields being non-empty; self-hosted deployments without
  legal env vars get enabled=false
- Add `enabled: boolean` to useLegalContact composable with false default;
  fix fetched-race by moving `fetched = true` into the .then() success
  handler so failed fetches can retry
- Wrap login page legal links in v-if="contact.enabled" so they only
  appear when legal contact is configured
- Guard contact-data sections in ImpressumView and controller block in
  PrivacyView with v-if="contact.name" to avoid blank sections

Co-Authored-By: Claude <noreply@anthropic.com>
- Replace boolean `fetched` flag with Promise-based deduplication in
  useLegalContact: concurrent callers share one in-flight Promise,
  failures clear the reference so the next caller can retry cleanly
- Use contact.enabled (all 6 fields present) instead of contact.name
  as the visibility guard in ImpressumView and PrivacyView, consistent
  with LoginView — partial configs (name set, email missing) no longer
  render incomplete contact blocks

Co-Authored-By: Claude <noreply@anthropic.com>
Check res.ok before parsing JSON so non-2xx responses don't silently
produce garbage state. Map each field explicitly with String()/Boolean()
coercion instead of blindly spreading the parsed payload.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
web/frontend/src/views/ImpressumView.vue (1)

17-41: ⚠️ Potential issue | 🟠 Major

Avoid silently serving an incomplete Impressum when contact data is unavailable.

At Line 17, v-if="contact.enabled" hides all mandatory legal identity/contact sections. If config/fetch fails, the page still renders with only disclaimer text, which is a compliance risk.

Proposed hardening
-    <template v-if="contact.enabled">
+    <template v-if="contact.enabled">
       <section>
         <h2>{{ t('legal.impressum_page.legal_info_title') }}</h2>
         <p>
           {{ contact.name }}<br>
           {{ contact.street }}<br>
           {{ contact.zip_city }}<br>
           {{ locale.current === 'de' ? contact.country_de : contact.country_en }}
         </p>
       </section>
@@
       <section>
         <h2>{{ t('legal.impressum_page.responsible_title') }}</h2>
         <p>
           {{ contact.name }}<br>
           {{ contact.street }}<br>
           {{ contact.zip_city }}
         </p>
       </section>
     </template>
+    <section v-else>
+      <h2>{{ t('legal.impressum_page.legal_info_title') }}</h2>
+      <p>{{ t('common.load_failed') }}</p>
+    </section>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/frontend/src/views/ImpressumView.vue` around lines 17 - 41, The template
currently only checks contact.enabled which lets the page render
partial/disclaimer content if contact fetch fails; add a computed/property
(e.g., isContactAvailable) in ImpressumView.vue that validates contact exists,
contact.enabled is true and required fields (name, street, zip_city, email) are
non-empty, then replace v-if="contact.enabled" with v-if="isContactAvailable";
if isContactAvailable is false, render a clear fallback/warning instead of the
partial Impressum (and guard uses of locale.current when selecting country text)
so incomplete contact data is never silently served.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/frontend/src/composables/useLegalContact.ts`:
- Around line 2-4: The header comment is incorrect about when the fetch runs;
update the comment for useLegalContact to state that the fetch is triggered on
the first invocation of useLegalContact() (lazy on-demand), not at module scope,
and clarify that it caches the result across subsequent calls and leaves empty
strings on fetch failure; reference the useLegalContact function and any
module-level cache variables to ensure the wording matches the actual
implementation.

In `@web/frontend/src/views/LoginView.vue`:
- Around line 6-7: The login view currently hides legal navigation when the
composable useLegalContact indicates contact.enabled is false; instead always
render/show legal links regardless of contact.enabled to avoid hiding legal
pages on transient API failure — update the logic in LoginView.vue (where
useLegalContact is used and where conditional rendering appears around
contact.enabled, including the other occurrences noted at the second occurrence
and lines ~139-146) to separate the legal navigation UI from contact.enabled
(use contact.enabled only for contact-specific controls, not for showing the
legal links), ensuring legal links are always visible to unauthenticated users.

In `@web/routes/legal.py`:
- Around line 16-34: The enabled calculation currently treats whitespace-only
config values as truthy; before computing enabled in the function that returns
LegalContactResponse, normalize each config field (config.legal_name,
config.legal_street, config.legal_zip_city, config.legal_country_en,
config.legal_country_de, config.legal_email) by trimming whitespace (e.g.,
.strip() or equivalent) and use the trimmed values to compute enabled and to
populate the LegalContactResponse fields so that fields containing only
whitespace are considered empty/false.

---

Duplicate comments:
In `@web/frontend/src/views/ImpressumView.vue`:
- Around line 17-41: The template currently only checks contact.enabled which
lets the page render partial/disclaimer content if contact fetch fails; add a
computed/property (e.g., isContactAvailable) in ImpressumView.vue that validates
contact exists, contact.enabled is true and required fields (name, street,
zip_city, email) are non-empty, then replace v-if="contact.enabled" with
v-if="isContactAvailable"; if isContactAvailable is false, render a clear
fallback/warning instead of the partial Impressum (and guard uses of
locale.current when selecting country text) so incomplete contact data is never
silently served.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 9d7dab47-0a8e-4043-a8db-e1fa7567e1a8

📥 Commits

Reviewing files that changed from the base of the PR and between 11d7c33 and f7d629e.

⛔ Files ignored due to path filters (1)
  • web/frontend/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (50)
  • .pre-commit-config.yaml
  • biome.json
  • web/frontend/package.json
  • web/frontend/src/api/client.ts
  • web/frontend/src/components/DiscordPicker.vue
  • web/frontend/src/components/HelloWorld.vue
  • web/frontend/src/components/InfoTooltip.vue
  • web/frontend/src/components/LanguageSwitcher.vue
  • web/frontend/src/components/LegalPageLayout.vue
  • web/frontend/src/components/MockupToolbar.vue
  • web/frontend/src/components/RealmPicker.vue
  • web/frontend/src/composables/useAutoSave.ts
  • web/frontend/src/composables/useLegalContact.ts
  • web/frontend/src/composables/useManualSave.ts
  • web/frontend/src/dev/fixtures.ts
  • web/frontend/src/dev/resolver.ts
  • web/frontend/src/i18n/index.ts
  • web/frontend/src/i18n/locales/de.ts
  • web/frontend/src/i18n/locales/en.ts
  • web/frontend/src/main.ts
  • web/frontend/src/router/index.ts
  • web/frontend/src/stores/auth.ts
  • web/frontend/src/utils/route.ts
  • web/frontend/src/views/ImpressumView.vue
  • web/frontend/src/views/LoginView.vue
  • web/frontend/src/views/PrivacyView.vue
  • web/frontend/src/views/TermsView.vue
  • web/frontend/src/views/guild/FormSubmissionsView.vue
  • web/frontend/src/views/guild/GuildDetailView.vue
  • web/frontend/src/views/guild/tabs/ApplicationFormsTab.vue
  • web/frontend/src/views/guild/tabs/ApplicationSubmissionsTab.vue
  • web/frontend/src/views/guild/tabs/ApplicationTemplatesTab.vue
  • web/frontend/src/views/guild/tabs/AutoDeleteTab.vue
  • web/frontend/src/views/guild/tabs/AutoKickerTab.vue
  • web/frontend/src/views/guild/tabs/LanguageTab.vue
  • web/frontend/src/views/guild/tabs/LeaveMessagesTab.vue
  • web/frontend/src/views/guild/tabs/ModeratorRolesTab.vue
  • web/frontend/src/views/guild/tabs/OperatorDashboardTab.vue
  • web/frontend/src/views/guild/tabs/OperatorGuildsTab.vue
  • web/frontend/src/views/guild/tabs/OperatorModulesTab.vue
  • web/frontend/src/views/guild/tabs/OperatorUserManagementTab.vue
  • web/frontend/src/views/guild/tabs/ReactionRolesTab.vue
  • web/frontend/src/views/guild/tabs/RemindersTab.vue
  • web/frontend/src/views/guild/tabs/RoleMappingsTab.vue
  • web/frontend/src/views/guild/tabs/ServerOverviewTab.vue
  • web/frontend/src/views/guild/tabs/SupportTab.vue
  • web/frontend/src/views/guild/tabs/WowCraftingTab.vue
  • web/frontend/src/views/guild/tabs/WowGuildNewsTab.vue
  • web/routes/legal.py
  • web/schemas.py

Comment thread web/frontend/src/composables/useLegalContact.ts Outdated
Comment thread web/frontend/src/views/LoginView.vue
Comment thread web/routes/legal.py Outdated
karaktaka and others added 3 commits March 15, 2026 00:49
- useLegalContact: fix comment — fetch is lazy (first call), not module-scope;
  clarify fetchPromise/contact caching and retry-on-failure behaviour
- LoginView: always render legal navigation links regardless of contact.enabled
  to avoid hiding them on transient API failures; remove now-unused useLegalContact
- legal.py: strip whitespace from all config fields before computing enabled
  and before populating the response, so whitespace-only values are treated as empty
- ImpressumView: add isContactAvailable computed that validates contact.enabled
  and required fields (name, street, zip_city, email) are non-empty; replace
  v-if="contact.enabled" with v-if="isContactAvailable"

Co-Authored-By: Claude <noreply@anthropic.com>
Replace the implicit enabled-from-all-fields heuristic with an explicit
NERPYBOT_WEB_LEGAL_ENABLED=true flag. When set, startup validates all six
contact fields via _require() and raises ValueError on the first missing one.
When unset (default), contact fields are ignored entirely.

This removes the ambiguity of "are legal pages on or off?" — the operator must
explicitly opt in. The per-request enabled computation in legal.py is replaced
by a direct config.legal_enabled read. LoginView restores v-if="contact.enabled"
since the flag is now unambiguous. docker-compose files updated with the new var.

Co-Authored-By: Claude <noreply@anthropic.com>
Both ImpressumView and PrivacyView need the same contact availability check
(enabled + required fields non-empty). Moving it into the composable and
returning it alongside contact ensures one definition is shared, so the two
views can't drift out of sync.

Co-Authored-By: Claude <noreply@anthropic.com>
@karaktaka karaktaka merged commit dbe6887 into main Mar 15, 2026
9 checks passed
@karaktaka karaktaka deleted the feat/legal-pages branch March 15, 2026 00:18
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant