diff --git a/ShareExtension/ShareViewController.swift b/ShareExtension/ShareViewController.swift index 2a3ee4b..b3c36a5 100644 --- a/ShareExtension/ShareViewController.swift +++ b/ShareExtension/ShareViewController.swift @@ -9,7 +9,7 @@ import UniformTypeIdentifiers /// - Empfängt `NSExtensionActivationSupportsText` + `WebURL`. /// - SwiftUI-Sheet mit Quote-Preview, Source-URL, „Als Draft /// speichern"-Button. -/// - POST an `https://zitare-api.mana.how/api/v1/share/receive` +/// - POST an `https://api.zitare.com/api/v1/share/receive` /// mit Envelope-Type `mana/text` (siehe `zitare/app-manifest.json`). /// - Bei Network-Failure: in `PendingShareStore` unter App-Group /// persistieren, die App synct beim nächsten Launch. diff --git a/Sources/Core/Auth/AppConfig.swift b/Sources/Core/Auth/AppConfig.swift index 48842b7..bf6f263 100644 --- a/Sources/Core/Auth/AppConfig.swift +++ b/Sources/Core/Auth/AppConfig.swift @@ -3,9 +3,13 @@ import ManaCore /// App-spezifische Konfiguration für Zitare. Implementiert /// `ManaAppConfig` aus ManaCore und ergänzt die Zitare-eigene -/// `apiBaseURL` (zitare-api, getrennt von mana-auth) sowie +/// `apiBaseURL` (api.zitare.com, getrennt von mana-auth) sowie /// `webBaseURL` (zitare.com, für WKWebView und Universal-Links) -/// und `appBaseURL` (zitare.mana.how, für eingeloggte Pfade). +/// und `appBaseURL` (app.zitare.com, für eingeloggte Pfade). +/// +/// Cutover zu .zitare.com-Subdomains am 2026-05-20. zitare.mana.how +/// ist abgeschaltet; zitare-api.mana.how bleibt als Back-Compat- +/// Surface erreichbar, der native Client nutzt sie aber nicht mehr. enum AppConfig { /// App-Group für Daten-Sharing zwischen App ↔ Widget ↔ ShareExt. /// Single-Source: wird in ``manaAppConfig`` und in @@ -21,24 +25,19 @@ enum AppConfig { appGroup: appGroup ) - /// `zitare-api.mana.how` — API-Backend (Hono+Bun). - static let apiBaseURL = URL(string: "https://zitare-api.mana.how")! + /// `api.zitare.com` — API-Backend (Hono+Bun). + static let apiBaseURL = URL(string: "https://api.zitare.com")! - /// `zitare.com` — geplante öffentliche Domain (CC-BY-SA-Korpus, - /// statisch). Universal-Link-Domain für AASA. **Heute DNS noch - /// nicht live** (Cloudflare-Zone-Onboarding offen, siehe - /// `zitare/STATUS.md`); bis dahin nutzt der WebView `appBaseURL` - /// (`zitare.mana.how`) — der Container liefert beide Surfaces. + /// `zitare.com` — öffentliche statische Korpus-Domain (CC-BY-SA). + /// Universal-Link-Domain für AASA. static let publicWebURL = URL(string: "https://zitare.com")! - /// `zitare.mana.how` — SPA-Surface, eingeloggte Pfade. Heute auch - /// der Default für Lese-Surfaces, bis `zitare.com` live ist. - static let appBaseURL = URL(string: "https://zitare.mana.how")! + /// `app.zitare.com` — SPA-Surface, eingeloggte Pfade (Submit, + /// Edit, Admin, Me). + static let appBaseURL = URL(string: "https://app.zitare.com")! - /// Effektive Default-URL für den WebView. Zeigt vorerst auf - /// `appBaseURL` (`zitare.mana.how`); nach Cloudflare-Zone-Cut - /// kommt das zurück auf `publicWebURL`. - static let webBaseURL = appBaseURL + /// Default-URL für den WebView (öffentliches Lese-Surface). + static let webBaseURL = publicWebURL /// Endpoint für den Korpus-Snapshot (Phase ζ-2). Heute noch nicht /// als statische HTTP-Datei publiziert — Aufgabe im Web-Repo: diff --git a/Sources/Core/WebShell/CookieBridge.swift b/Sources/Core/WebShell/CookieBridge.swift index 380ea4e..52ae601 100644 --- a/Sources/Core/WebShell/CookieBridge.swift +++ b/Sources/Core/WebShell/CookieBridge.swift @@ -3,7 +3,7 @@ import ManaCore import WebKit /// Reicht den mana-auth-JWT als Cookie an den `WKWebView` weiter, sodass -/// eingeloggte `(app)`-Routen auf `zitare.mana.how` ohne zweiten Login +/// eingeloggte `(app)`-Routen auf `app.zitare.com` ohne zweiten Login /// erreichbar sind. /// /// **Phase ζ-1: Skeleton.** Methoden existieren, werden aber heute @@ -13,14 +13,19 @@ import WebKit /// *echten* mana-auth-Token End-to-End getestet sein (Verifikations- /// Lücke in `zitare/STATUS.md`). /// -/// **Phase ζ-3:** wird in `SubmitQuoteView` benutzt — vor dem POST -/// gegen `zitare-api.mana.how` und vor dem Öffnen von -/// `zitare.mana.how/me` im WebView. +/// **Cross-Domain-Hinweis 2026-05-20:** Mana-Auth setzt den Cookie +/// auf `.mana.how`. Seit dem Zitare-Cutover auf `app.zitare.com` +/// kann dieser Cookie nicht mehr direkt geteilt werden. Der WebView- +/// Auth-Pfad braucht stattdessen Bearer-Token-Injection (siehe +/// Web-App `apps/zitare/src/lib/auth.ts` Pattern: Cross-Origin +/// /refresh gegen auth.mana.how mit credentials:include) oder ein +/// dediziertes Cookie auf `.zitare.com` via mana-auth-Erweiterung. +/// Diese Klasse ist noch nicht angepasst — Update kommt vor ζ-3. /// /// **Cookie-Schema** (gespiegelt zu mana-auth, siehe /// `mana/services/mana-auth/src/auth/cookies.ts`): /// - Name: `mana.access` (JWT) und optional `mana.refresh` (Opaque) -/// - Domain: `.mana.how` (App-Surface; **nicht** `.com`) +/// - Domain: `.mana.how` (greift heute nicht für `.zitare.com`) /// - Path: `/` /// - Secure: true, HTTPOnly: false (WebView muss lesen können), /// SameSite: Lax diff --git a/Tests/UnitTests/AppConfigTests.swift b/Tests/UnitTests/AppConfigTests.swift index c623037..e2beb0b 100644 --- a/Tests/UnitTests/AppConfigTests.swift +++ b/Tests/UnitTests/AppConfigTests.swift @@ -17,20 +17,18 @@ final class AppConfigTests: XCTestCase { } func test_apiBaseURL_pointsToZitareApi() { - XCTAssertEqual(AppConfig.apiBaseURL.absoluteString, "https://zitare-api.mana.how") + XCTAssertEqual(AppConfig.apiBaseURL.absoluteString, "https://api.zitare.com") } - func test_webBaseURL_currentDefault() { - // Übergang: zitare.com hat noch keinen DNS-Record (Cloudflare-Zone- - // Onboarding offen), deshalb fällt webBaseURL aktuell auf - // appBaseURL zurück. Nach Cloudflare-Cut wird das wieder - // publicWebURL — Test dann anpassen. - XCTAssertEqual(AppConfig.webBaseURL.absoluteString, "https://zitare.mana.how") + func test_webBaseURL_isPublicCom() { + // Cutover 2026-05-20: zitare.com ist live, webBaseURL zeigt + // auf das öffentliche Lese-Surface. + XCTAssertEqual(AppConfig.webBaseURL.absoluteString, "https://zitare.com") XCTAssertEqual(AppConfig.publicWebURL.absoluteString, "https://zitare.com") } - func test_appBaseURL_isManaHowSurface() { - XCTAssertEqual(AppConfig.appBaseURL.absoluteString, "https://zitare.mana.how") + func test_appBaseURL_isAppZitareCom() { + XCTAssertEqual(AppConfig.appBaseURL.absoluteString, "https://app.zitare.com") } func test_appGroup_matchesEntitlement() { diff --git a/Tests/UnitTests/DeepLinkRouterTests.swift b/Tests/UnitTests/DeepLinkRouterTests.swift index 2d5fe19..16f43ba 100644 --- a/Tests/UnitTests/DeepLinkRouterTests.swift +++ b/Tests/UnitTests/DeepLinkRouterTests.swift @@ -2,32 +2,32 @@ import XCTest @testable import ZitareNative final class DeepLinkRouterTests: XCTestCase { - let base = URL(string: "https://zitare.mana.how")! + let base = URL(string: "https://zitare.com")! // MARK: - resolveToWebURL func test_resolve_customSchemeQuote() throws { let url = try XCTUnwrap(URL(string: "zitare://quote/spitteler-schweizer-bleiben")) let resolved = DeepLinkRouter.resolveToWebURL(url, base: base) - XCTAssertEqual(resolved.absoluteString, "https://zitare.mana.how/q/spitteler-schweizer-bleiben") + XCTAssertEqual(resolved.absoluteString, "https://zitare.com/q/spitteler-schweizer-bleiben") } func test_resolve_customSchemeAuthor() throws { let url = try XCTUnwrap(URL(string: "zitare://author/spitteler")) let resolved = DeepLinkRouter.resolveToWebURL(url, base: base) - XCTAssertEqual(resolved.absoluteString, "https://zitare.mana.how/a/spitteler") + XCTAssertEqual(resolved.absoluteString, "https://zitare.com/a/spitteler") } func test_resolve_customSchemeCollection() throws { let url = try XCTUnwrap(URL(string: "zitare://collection/schweizer-stimmen")) let resolved = DeepLinkRouter.resolveToWebURL(url, base: base) - XCTAssertEqual(resolved.absoluteString, "https://zitare.mana.how/c/schweizer-stimmen") + XCTAssertEqual(resolved.absoluteString, "https://zitare.com/c/schweizer-stimmen") } func test_resolve_unknownCustomSchemeFallsBackToBase() throws { let url = try XCTUnwrap(URL(string: "zitare://unknown/foo")) let resolved = DeepLinkRouter.resolveToWebURL(url, base: base) - XCTAssertEqual(resolved.absoluteString, "https://zitare.mana.how") + XCTAssertEqual(resolved.absoluteString, "https://zitare.com") } func test_resolve_httpsPassesThrough() throws { @@ -82,7 +82,7 @@ final class DeepLinkRouterTests: XCTestCase { func test_route_httpsExplorePath_goesToExplore() throws { let result = try DeepLinkRouter.route( - XCTUnwrap(URL(string: "https://zitare.mana.how/region/schweiz")), + XCTUnwrap(URL(string: "https://zitare.com/region/schweiz")), base: base ) XCTAssertTrue(result.isExplore) diff --git a/project.yml b/project.yml index d2a0c61..5698a11 100644 --- a/project.yml +++ b/project.yml @@ -92,7 +92,7 @@ targets: - zitare NSUserActivityTypes: - NSUserActivityTypeBrowsingWeb - # WKWebView lädt zitare.com + zitare.mana.how. Beide sind + # WKWebView lädt zitare.com + app.zitare.com. Beide sind # https, kein App-Transport-Security-Override nötig. ITSAppUsesNonExemptEncryption: false entitlements: @@ -104,18 +104,13 @@ targets: keychain-access-groups: - $(AppIdentifierPrefix)ev.mana.session # Universal-Link-Domains: - # - zitare.com ist die kanonische Production-Domain (steht - # auch im Manifest und im AASA-File auf zitare-com). - # - zitare.mana.how als Staging-Domain, solange die zitare.com- - # Cloudflare-Zone noch nicht aufgelöst wird (DNS-Setup - # blockiert auf separatem cloudflared-Login). Nach Cloudflare- - # Cut kann der Eintrag bleiben — schadet nicht, deckt einfach - # beide Hosts ab. - # Beide Hosts liefern identische AASA-Files (siehe nginx-app.conf - # bzw. nginx-com.conf im zitare-Repo). + # - zitare.com ist die kanonische öffentliche Domain (statisch, + # liefert AASA). + # - app.zitare.com ist das eingeloggte App-Surface (SPA, + # liefert AASA-File analog). com.apple.developer.associated-domains: - applinks:zitare.com - - applinks:zitare.mana.how + - applinks:app.zitare.com com.apple.security.application-groups: - group.ev.mana.zitare settings: