diff --git a/apps/rebreak-magic-mac/Sources/Services/MagicAPIClient.swift b/apps/rebreak-magic-mac/Sources/Services/MagicAPIClient.swift index b779358..c49aba8 100644 --- a/apps/rebreak-magic-mac/Sources/Services/MagicAPIClient.swift +++ b/apps/rebreak-magic-mac/Sources/Services/MagicAPIClient.swift @@ -8,7 +8,13 @@ struct MagicRegistration: Codable { let existing: Bool } +enum MagicDeviceSource: String, Codable { + case magic + case protected +} + struct MagicDevice: Codable, Identifiable { + let source: MagicDeviceSource? let deviceId: String let hostname: String let model: String? @@ -16,8 +22,11 @@ struct MagicDevice: Codable, Identifiable { let magicEnrolledAt: String let releaseRequestedAt: String? let releaseAvailableAt: String? - - var id: String { deviceId } + + var id: String { "\(source?.rawValue ?? "magic"):\(deviceId)" } + + /// Default zu `.magic` falls Backend (alte Version) das Feld nicht setzt. + var resolvedSource: MagicDeviceSource { source ?? .magic } var enrolledDate: Date? { ISO8601DateFormatter().date(from: magicEnrolledAt) diff --git a/apps/rebreak-magic-mac/Sources/Views/DeviceHubView.swift b/apps/rebreak-magic-mac/Sources/Views/DeviceHubView.swift index 37ac58a..438480b 100644 --- a/apps/rebreak-magic-mac/Sources/Views/DeviceHubView.swift +++ b/apps/rebreak-magic-mac/Sources/Views/DeviceHubView.swift @@ -279,8 +279,19 @@ private struct HubDeviceRow: View { .frame(width: 28) VStack(alignment: .leading, spacing: 2) { - Text(device.hostname) - .font(.callout.bold()) + HStack(spacing: 6) { + Text(device.hostname) + .font(.callout.bold()) + if device.resolvedSource == .protected { + Text("Native-App") + .font(.caption2.bold()) + .padding(.horizontal, 6) + .padding(.vertical, 2) + .background(Color.gray.opacity(0.15)) + .foregroundStyle(.secondary) + .clipShape(Capsule()) + } + } HStack(spacing: 8) { if let model = device.model { Text(model).font(.caption2).foregroundStyle(.secondary) @@ -294,7 +305,11 @@ private struct HubDeviceRow: View { Spacer() - if device.isReleasing { + if device.resolvedSource == .protected { + Text("Verwaltung in der ReBreak-App") + .font(.caption2) + .foregroundStyle(.tertiary) + } else if device.isReleasing { VStack(alignment: .trailing, spacing: 2) { Label("Freigabe läuft", systemImage: "hourglass") .font(.caption2.bold()) diff --git a/apps/rebreak-magic-mac/Sources/Views/MacRegistrationView.swift b/apps/rebreak-magic-mac/Sources/Views/MacRegistrationView.swift index 6ac62e5..f399f80 100644 --- a/apps/rebreak-magic-mac/Sources/Views/MacRegistrationView.swift +++ b/apps/rebreak-magic-mac/Sources/Views/MacRegistrationView.swift @@ -221,7 +221,7 @@ struct MacRegistrationView: View { await MainActor.run { isInstallingProfile = false - successMessage = "System Settings → Profile geöffnet. Bitte dort „Installieren" klicken und Admin-Passwort eingeben." + successMessage = "System Settings → Profile geöffnet. Bitte dort „Installieren“ klicken und Admin-Passwort eingeben." } // Re-check profile status nach kurzer Wartezeit (User muss in UI bestätigen) diff --git a/apps/rebreak-native/app.config.ts b/apps/rebreak-native/app.config.ts index b750ef2..131e3d2 100644 --- a/apps/rebreak-native/app.config.ts +++ b/apps/rebreak-native/app.config.ts @@ -36,7 +36,7 @@ export default ({ config }: ConfigContext): ExpoConfig => ({ ios: { supportsTablet: true, bundleIdentifier: MAIN_BUNDLE, - buildNumber: "67", + buildNumber: "68", // Apple Sign-In Entitlement — Pflicht für expo-apple-authentication nativen // signInAsync()-Flow. Ohne flag generiert Expo's prebuild den // com.apple.developer.applesignin-Entitlement nicht in die .entitlements. @@ -59,7 +59,7 @@ export default ({ config }: ConfigContext): ExpoConfig => ({ android: { package: "org.rebreak.app", - versionCode: 50, + versionCode: 51, adaptiveIcon: { // Foreground muss in der ~66%-Safe-Zone bleiben (Launcher-Mask clippt den // Außenring) → adaptive-foreground.png ist das Logo auf transparentem diff --git a/apps/rebreak-native/modules/rebreak-protection/ios/RebreakContentFilter/Info.plist b/apps/rebreak-native/modules/rebreak-protection/ios/RebreakContentFilter/Info.plist index fb8d354..5e6716b 100644 --- a/apps/rebreak-native/modules/rebreak-protection/ios/RebreakContentFilter/Info.plist +++ b/apps/rebreak-native/modules/rebreak-protection/ios/RebreakContentFilter/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 0.3.13 CFBundleVersion - 67 + 68 NSExtension NSExtensionPointIdentifier diff --git a/apps/rebreak-native/modules/rebreak-protection/ios/RebreakPacketTunnelExtension/Info.plist b/apps/rebreak-native/modules/rebreak-protection/ios/RebreakPacketTunnelExtension/Info.plist index 3db75c4..6d7a147 100644 --- a/apps/rebreak-native/modules/rebreak-protection/ios/RebreakPacketTunnelExtension/Info.plist +++ b/apps/rebreak-native/modules/rebreak-protection/ios/RebreakPacketTunnelExtension/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 0.3.13 CFBundleVersion - 67 + 68 NSExtension NSExtensionPointIdentifier diff --git a/apps/rebreak-native/modules/rebreak-protection/ios/RebreakURLFilterExtension/Info.plist b/apps/rebreak-native/modules/rebreak-protection/ios/RebreakURLFilterExtension/Info.plist index 44ba003..205e74f 100644 --- a/apps/rebreak-native/modules/rebreak-protection/ios/RebreakURLFilterExtension/Info.plist +++ b/apps/rebreak-native/modules/rebreak-protection/ios/RebreakURLFilterExtension/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 0.3.13 CFBundleVersion - 67 + 68 EXAppExtensionAttributes EXExtensionPointIdentifier diff --git a/backend/server/api/magic/devices.get.ts b/backend/server/api/magic/devices.get.ts index 0480d52..fd8ee31 100644 --- a/backend/server/api/magic/devices.get.ts +++ b/backend/server/api/magic/devices.get.ts @@ -1,18 +1,27 @@ import { listMagicDevices } from "../../db/devices"; +import { listProtectedDevices } from "../../db/protectedDevices"; import { requireUser } from "../../utils/auth"; /** * GET /api/magic/devices * - * Listet alle aktiven Magic-Bindings des Users für UI. - * Response: [{ deviceId, hostname, model, osVersion, magicEnrolledAt, releaseRequestedAt, releaseAvailableAt }] + * Listet alle gesch\u00fctzten Ger\u00e4te des Users f\u00fcr den Magic-Hub. Vereinigt: + * - Magic-Bindings (UserDevice.magicEnrolledAt) \u2014 via Magic-App registriert + * - ProtectedDevices \u2014 alter Native-App-DNS-Schutz-Flow (Multi-Device) + * + * Response-Items haben ein `source`-Flag: + * "magic" \u2192 voll verwaltet, unterst\u00fctzt request-release + * "protected" \u2192 alter Flow, nur Anzeige + revoke (TODO: own action) */ export default defineEventHandler(async (event) => { const user = await requireUser(event); - const devices = await listMagicDevices(user.id); - // Berechne releaseAvailableAt (releaseRequestedAt + 24h) - const enriched = devices.map((d) => { + const [magic, protectedDevices] = await Promise.all([ + listMagicDevices(user.id), + listProtectedDevices(user.id), + ]); + + const magicItems = magic.map((d) => { let releaseAvailableAt: string | null = null; if (d.releaseRequestedAt) { const availableAt = new Date( @@ -22,8 +31,9 @@ export default defineEventHandler(async (event) => { } return { + source: "magic" as const, deviceId: d.deviceId, - hostname: d.hostname, + hostname: d.hostname ?? "Unbenanntes Ger\u00e4t", model: d.model, osVersion: d.osVersion, magicEnrolledAt: d.magicEnrolledAt.toISOString(), @@ -32,8 +42,20 @@ export default defineEventHandler(async (event) => { }; }); + const protectedItems = protectedDevices.map((d) => ({ + source: "protected" as const, + deviceId: d.id, + hostname: d.label, + model: d.platform, + osVersion: null as string | null, + magicEnrolledAt: (d.installedAt ?? d.createdAt).toISOString(), + releaseRequestedAt: null as string | null, + releaseAvailableAt: null as string | null, + })); + + // Magic-Bindings zuerst (neuste), dann alte ProtectedDevices return { success: true, - data: enriched, + data: [...magicItems, ...protectedItems], }; });