Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Packages/App/Sources/AppUIMain/Domain/Issue+Metadata.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import Foundation

extension Issue {
struct Metadata {
let ctx: PartoutContext
let ctx: PartoutLoggerContext

let profile: Profile?

Expand All @@ -49,7 +49,7 @@ extension Issue {

@MainActor
static func withMetadata(_ metadata: Metadata) async -> Issue {
let appLog = metadata.ctx.currentLog(parameters: metadata.parameters)
let appLog = metadata.ctx.logger.currentLog(parameters: metadata.parameters)
.joined(separator: "\n")
.data(using: .utf8)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,18 +51,7 @@ struct DiagnosticsView: View {

let tunnel: ExtendedTunnel

var availableTunnelLogs: () async -> [LogEntry] = {
await Task.detached {
LocalLogger.FileStrategy()
.availableLogs(at: BundleConfiguration.urlForTunnelLog)
.sorted {
$0.key > $1.key
}
.map {
LogEntry(date: $0, url: $1)
}
}.value
}
var availableTunnelLogs: (() async -> [LogEntry])?

@State
private var logsPrivateData = false
Expand Down Expand Up @@ -92,7 +81,7 @@ struct DiagnosticsView: View {
}
}
.task {
tunnelLogs = await availableTunnelLogs()
tunnelLogs = await computedTunnelLogs()
}
.themeKeyValue(kvStore, AppPreference.logsPrivateData.key, $logsPrivateData, default: false)
.themeForm()
Expand Down Expand Up @@ -183,6 +172,24 @@ private extension DiagnosticsView {
}

private extension DiagnosticsView {
func computedTunnelLogs() async -> [LogEntry] {
await (availableTunnelLogs ?? defaultTunnelLogs)()
}

func defaultTunnelLogs() async -> [LogEntry] {
await Task.detached {
// FIXME: #1373, diagnostics/logs must be per-tunnel
LocalLogger.FileStrategy()
.availableLogs(at: BundleConfiguration.urlForTunnelLog)
.sorted {
$0.key > $1.key
}
.map {
LogEntry(date: $0, url: $1)
}
}.value
}

func removeTunnelLog(at url: URL) {
guard let firstIndex = tunnelLogs.firstIndex(where: { $0.url == url }) else {
return
Expand All @@ -199,9 +206,11 @@ private extension DiagnosticsView {
}

func removeTunnelLogs() {
LocalLogger.FileStrategy().purgeLogs(at: BundleConfiguration.urlForTunnelLog)
// FIXME: #1373, diagnostics/logs must be per-tunnel
LocalLogger.FileStrategy()
.purgeLogs(at: BundleConfiguration.urlForTunnelLog)
Task {
tunnelLogs = await availableTunnelLogs()
tunnelLogs = await computedTunnelLogs()
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,12 @@ private extension Issue {
var items: [Any] {
var list: [Any] = []
list.append(body)
if let appLog, let url = appLog.toTemporaryURL(withFilename: Constants.shared.log.appPath) {
if let appLog,
let url = appLog.toTemporaryURL(withFilename: Constants.shared.log.appPath) {
list.append(url)
}
if let tunnelLog, let url = tunnelLog.toTemporaryURL(withFilename: Constants.shared.log.tunnelPath) {
if let tunnelLog,
let url = tunnelLog.toTemporaryURL(withFilename: Constants.shared.log.tunnelPath) {
list.append(url)
}
return list
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import Foundation
public final class KeyValueManager: ObservableObject {
private var store: KeyValueStore

public var fallback: [String: Any]
private let fallback: [String: Any]

public init(store: KeyValueStore = InMemoryStore(), fallback: [String: Any] = [:]) {
self.store = store
Expand Down
114 changes: 26 additions & 88 deletions Packages/App/Sources/CommonLibrary/CommonLibrary.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,96 +23,34 @@
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//

import Foundation
@_exported import Partout

@MainActor
public final class CommonLibrary {
public enum Target {
case app

case tunnel(Profile.ID)
}

private let kvStore: KeyValueManager

public init(kvStore: KeyValueManager) {
self.kvStore = kvStore
}

public func configurePartout(forTarget target: Target) -> PartoutContext {
switch target {
case .app:
return configureApp()
case .tunnel(let profileId):
return configureTunnel(profileId: profileId)
}
}
}

private extension CommonLibrary {
func configureApp() -> PartoutContext {
configureShared()

var ctxBuilder = PartoutContext.Builder()
let logsPrivateData = kvStore.bool(forKey: AppPreference.logsPrivateData.key)
ctxBuilder.configureLogging(
to: BundleConfiguration.urlForAppLog,
parameters: Constants.shared.log,
logsPrivateData: logsPrivateData
)
let ctx = ctxBuilder.build()
PartoutContext.register(ctx)

ctx.logPreamble(parameters: Constants.shared.log)
return ctx
}

func configureTunnel(profileId: Profile.ID) -> PartoutContext {
configureShared()

var ctxBuilder = PartoutContext.Builder(profileId: profileId)
// FIXME: #1374, AppPreference not accessible by sysex
let logsPrivateData = kvStore.bool(forKey: AppPreference.logsPrivateData.key)
ctxBuilder.configureLogging(
to: BundleConfiguration.urlForTunnelLog,
parameters: Constants.shared.log,
logsPrivateData: logsPrivateData
)
// FIXME: #1374, AppPreference not accessible by sysex
if kvStore.bool(forKey: AppPreference.dnsFallsBack.key) {
ctxBuilder.dnsFallbackServers = Constants.shared.tunnel.dnsFallbackServers
}
let ctx = ctxBuilder.build()
PartoutContext.register(ctx)

ctx.logPreamble(parameters: Constants.shared.log)
if let dnsFallbackServers = ctx.dnsFallbackServers {
pp_log(ctx, .app, .info, "Enable DNS fallback servers: \(dnsFallbackServers)")
}
return ctx
}

func configureShared() {
kvStore.fallback = [
AppPreference.dnsFallsBack.key: true,
AppPreference.logsPrivateData.key: false
]
}
}

private extension PartoutContext {
func logPreamble(parameters: Constants.Log) {
appendLog(parameters.options.maxLevel, message: "")
appendLog(parameters.options.maxLevel, message: "--- BEGIN ---")
appendLog(parameters.options.maxLevel, message: "")

let systemInfo = SystemInformation()
appendLog(parameters.options.maxLevel, message: "App: \(BundleConfiguration.mainVersionString)")
appendLog(parameters.options.maxLevel, message: "OS: \(systemInfo.osString)")
if let deviceString = systemInfo.deviceString {
appendLog(parameters.options.maxLevel, message: "Device: \(deviceString)")
public enum CommonLibrary {
public static func assertMissingImplementations(with registry: Registry) {
ModuleType.allCases.forEach { moduleType in
let builder = moduleType.newModule(with: registry)
do {
// ModuleBuilder -> Module
let module = try builder.tryBuild()

// Module -> ModuleBuilder
guard let moduleBuilder = module.moduleBuilder() else {
fatalError("\(moduleType): does not produce a ModuleBuilder")
}

// AppFeatureRequiring
guard builder is any AppFeatureRequiring else {
fatalError("\(moduleType): #1 is not AppFeatureRequiring")
}
guard moduleBuilder is any AppFeatureRequiring else {
fatalError("\(moduleType): #2 is not AppFeatureRequiring")
}
} catch {
if (error as? PartoutError)?.code == .incompleteModule {
return
}
fatalError("\(moduleType): empty module is not buildable: \(error)")
}
}
appendLog(parameters.options.maxLevel, message: "")
}
}
15 changes: 15 additions & 0 deletions Packages/App/Sources/CommonLibrary/Domain/AppPreference.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,18 @@ public enum AppPreference: String, PreferenceProtocol {
"App.\(rawValue)"
}
}

public struct AppPreferenceValues: Sendable {
public var dnsFallsBack = false

public var skipsPurchases = false

public var lastInfrastructureRefresh: Date?

public var lastUsedProfileId: Profile.ID?

public var logsPrivateData = false

public init() {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//
// KeyValueManager+AppPreference.swift
// Passepartout
//
// Created by Davide De Rosa on 5/17/25.
// Copyright (c) 2025 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//

import CommonUtils
import Foundation

extension KeyValueManager {
public var preferences: AppPreferenceValues {
var values = AppPreferenceValues()
values.dnsFallsBack = bool(forKey: AppPreference.dnsFallsBack.key)
values.skipsPurchases = bool(forKey: AppPreference.skipsPurchases.key)
values.lastInfrastructureRefresh = object(forKey: AppPreference.lastInfrastructureRefresh.key)
values.lastUsedProfileId = object(forKey: AppPreference.lastUsedProfileId.key)
values.logsPrivateData = bool(forKey: AppPreference.logsPrivateData.key)
return values
}

public convenience init(store: KeyValueStore, fallback: AppPreferenceValues) {
let values = [
AppPreference.dnsFallsBack.key: fallback.dnsFallsBack,
AppPreference.logsPrivateData.key: fallback.logsPrivateData
]
self.init(store: store, fallback: values)
}
}

This file was deleted.

Loading