feat: app.zitare.com + api.zitare.com URLs für Cutover 2026-05-20
Native-Konstanten ziehen mit dem Web-Cutover zu .zitare.com- Subdomains nach. Universal-Link-AASA-Liste enthält jetzt zitare.com + app.zitare.com (zitare.mana.how raus). webBaseURL ist jetzt das echte publicWebURL (zitare.com), wie ursprünglich geplant. CookieBridge bleibt Skeleton — die `.mana.how`-Cookie-Domain- Strategie greift nicht für `.zitare.com`. Hinweis im Kommentar gesetzt, Update vor ζ-3 nötig. Code-Only. Erst nach nächstem Native-Build/TestFlight-Upload live. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
8b572329fc
commit
a4ea32b637
6 changed files with 45 additions and 48 deletions
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
17
project.yml
17
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:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue