MarkBase架构升级:Multi-Volume Virtual Tree + Dual-View Management + Git Remote修正
核心功能: - ✅ Categories/Series双视图管理(category_view.rs + import_markdown.rs) - ✅ FUSE Multi-Volume支持(tree_type参数) - ✅ SSH/SFTP/SCP/rsync协议完整实现(4042行) - ✅ NFS/SMB Module Phase 1-3完成 - ✅ Archive Module Phase 1-4完成(2916行) - ✅ Download Center API完整实现 - ✅ S3兼容API实现(560行) Git配置修正: - ✅ 删除错误origin(gitea.momentry.ddns.net) - ✅ 删除m5max128(指向机器名) - ✅ 设置origin = m5max128gitea.momentry.ddns.net/admin/markbase - ✅ 设置m4minigitea = m4minigitea.momentry.ddns.net/warren/markbase 数据清理: - ✅ 删除38个临时SQLite(保留accusys.sqlite、demo.sqlite) - ✅ 删除.bak、test_*.bin、调试脚本等临时文件 - ✅ 删除临时目录(build/、download files/、raid_test/等) - ✅ 更新.gitignore排除临时文件 架构优化: - 52个文件修改,2434行新增,4739行删除 - Workspace成员整合(16个crate) - 数据库状态:accusys.sqlite保留(主demo测试) 远程同步: - ✅ 准备推送到m5max128gitea(远程Gitea) - ✅ 准备推送到m4minigitea(本地Gitea)
This commit is contained in:
34
MarkBaseFS/MarkBaseFSApp/Contents/Info.plist
Normal file
34
MarkBaseFS/MarkBaseFSApp/Contents/Info.plist
Normal file
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>MarkBaseFS</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string></string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.accusys.markbase.app</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>MarkBaseFS</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>14.0</string>
|
||||
<key>LSUIElement</key>
|
||||
<true/>
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<true/>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2026 Accusys, Inc. All rights reserved.</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
</dict>
|
||||
</plist>
|
||||
Binary file not shown.
@@ -0,0 +1,100 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>BuildMachineOSBuild</key>
|
||||
<string>25F71</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>MarkBaseFS</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>com.accusys.markbase.fskitmodule</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.accusys.markbase.fskitmodule</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>MarkBaseFS FSKit Module</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XPC!</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSupportedPlatforms</key>
|
||||
<array>
|
||||
<string>MacOSX</string>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0.0</string>
|
||||
<key>DTCompiler</key>
|
||||
<string>com.apple.compilers.llvm.clang.1_0</string>
|
||||
<key>DTPlatformBuild</key>
|
||||
<string>25F70</string>
|
||||
<key>DTPlatformName</key>
|
||||
<string>macosx</string>
|
||||
<key>DTPlatformVersion</key>
|
||||
<string>26.5</string>
|
||||
<key>DTSDKBuild</key>
|
||||
<string>25F70</string>
|
||||
<key>DTSDKName</key>
|
||||
<string>macosx26.5</string>
|
||||
<key>DTXcode</key>
|
||||
<string>2650</string>
|
||||
<key>DTXcodeBuild</key>
|
||||
<string>17F42</string>
|
||||
<key>EXAppExtensionAttributes</key>
|
||||
<dict>
|
||||
<key>EXExtensionPointIdentifier</key>
|
||||
<string>com.apple.fskit.fsmodule</string>
|
||||
<key>EXExtensionPrincipalClass</key>
|
||||
<string>MarkBaseFSModule</string>
|
||||
<key>FSMediaTypes</key>
|
||||
<dict>
|
||||
<key>B8A9778D-F1AF-41A0-B7FF-C360A7878CD3</key>
|
||||
<dict>
|
||||
<key>FSMediaProperties</key>
|
||||
<dict>
|
||||
<key>Content Hint</key>
|
||||
<string>B8A9778D-F1AF-41A0-B7FF-C360A7878CD3</string>
|
||||
<key>Leaf</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>FSProbeArguments</key>
|
||||
<string>-p</string>
|
||||
<key>FSProbeExecutable</key>
|
||||
<string>MarkBaseFSProbe</string>
|
||||
<key>FSProbeOrder</key>
|
||||
<integer>2000</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>FSPersonalities</key>
|
||||
<dict>
|
||||
<key>MarkBaseFS</key>
|
||||
<dict>
|
||||
<key>FSFormatOptions</key>
|
||||
<dict>
|
||||
<key>shortOptions</key>
|
||||
<string>NRI:S:a:b:c:n:s:v:</string>
|
||||
</dict>
|
||||
<key>FSMountOptions</key>
|
||||
<dict>
|
||||
<key>shortOptions</key>
|
||||
<string>dnqS:y</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>FSRequiresSecurityScopedPathURLResources</key>
|
||||
<false/>
|
||||
<key>FSShortName</key>
|
||||
<string>markbasefs</string>
|
||||
<key>FSSupportedSchemes</key>
|
||||
<array>
|
||||
<string>markbasefs</string>
|
||||
</array>
|
||||
<key>FSSupportsBlockResources</key>
|
||||
<false/>
|
||||
<key>FSSupportsGenericURLResources</key>
|
||||
<true/>
|
||||
<key>FSSupportsPathURLs</key>
|
||||
<false/>
|
||||
</dict>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>15.4</string>
|
||||
</dict>
|
||||
</plist>
|
||||
Binary file not shown.
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.accusys.markbase.fskitmodule</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>MarkBaseFS FSKit Module</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>MarkBaseFS</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0.0</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XPC!</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>com.accusys.markbase.fskitmodule</string>
|
||||
<key>EXAppExtensionAttributes</key>
|
||||
<dict>
|
||||
<key>EXExtensionPointIdentifier</key>
|
||||
<string>com.apple.fskit.fsmodule</string>
|
||||
<key>EXExtensionPrincipalClass</key>
|
||||
<string>MarkBaseFSModule</string>
|
||||
<key>FSShortName</key>
|
||||
<string>markbasefs</string>
|
||||
<key>FSMediaTypes</key>
|
||||
<dict/>
|
||||
<key>FSPersonalities</key>
|
||||
<dict/>
|
||||
<key>FSSupportedSchemes</key>
|
||||
<array>
|
||||
<string>markbasefs</string>
|
||||
</array>
|
||||
<key>FSSupportsBlockResources</key>
|
||||
<false/>
|
||||
<key>FSSupportsGenericURLResources</key>
|
||||
<true/>
|
||||
<key>FSSupportsPathURLs</key>
|
||||
<false/>
|
||||
<key>FSRequiresSecurityScopedPathURLResources</key>
|
||||
<false/>
|
||||
</dict>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>15.4</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,61 @@
|
||||
import Foundation
|
||||
import FSKit
|
||||
|
||||
@available(macOS 15.4, *)
|
||||
public class MarkBaseFSModule: NSObject, FSUnaryFileSystemOperations, @unchecked Sendable {
|
||||
|
||||
// MarkBaseFS FSKit Module Entry Point
|
||||
// Implements FSUnaryFileSystemOperations protocol
|
||||
|
||||
public required override init() {
|
||||
super.init()
|
||||
print("MarkBaseFSModule initializing...")
|
||||
}
|
||||
|
||||
// MARK: - FSUnaryFileSystemOperations
|
||||
|
||||
public func probeResource(_ resource: FSResource, replyHandler: @escaping (FSProbeResult?, Error?) -> Void) {
|
||||
print("MarkBaseFSModule probeResource() called")
|
||||
|
||||
// Create probe result
|
||||
let result = FSProbeResult()
|
||||
result.matchResult = .usable
|
||||
|
||||
print(" - Resource probe complete: usable")
|
||||
|
||||
replyHandler(result, nil)
|
||||
}
|
||||
|
||||
public func loadResource(_ resource: FSResource, options: FSTaskOptions, replyHandler: @escaping (FSVolume?, Error?) -> Void) {
|
||||
print("MarkBaseFSModule loadResource() called")
|
||||
|
||||
// Create Volume.Identifier
|
||||
let volumeID = FSVolume.Identifier()
|
||||
|
||||
// Create Volume Name
|
||||
let volumeName = FSFileName(string: "MarkBaseFS")
|
||||
|
||||
// Create Volume (using MarkBaseFSVolumeFSKit)
|
||||
let volume = MarkBaseFSVolumeFSKit(volumeID: volumeID, volumeName: volumeName)
|
||||
|
||||
print(" - Volume created: \(volumeID.uuid)")
|
||||
|
||||
replyHandler(volume, nil)
|
||||
}
|
||||
|
||||
public func unloadResource(_ resource: FSResource, options: FSTaskOptions, replyHandler: @escaping (Error?) -> Void) {
|
||||
print("MarkBaseFSModule unloadResource() called")
|
||||
|
||||
print(" - Resource unloaded successfully")
|
||||
|
||||
replyHandler(nil)
|
||||
}
|
||||
|
||||
// MARK: - Optional Methods
|
||||
|
||||
public func didFinishLoading() {
|
||||
print("MarkBaseFSModule didFinishLoading() called")
|
||||
print(" - Module loaded by FSKit daemon")
|
||||
print(" - Ready to receive FSKit requests")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import Foundation
|
||||
import FSKit
|
||||
|
||||
@available(macOS 15.4, *)
|
||||
public class MarkBaseFSVolumeFSKit: FSVolume, @unchecked Sendable {
|
||||
|
||||
// MarkBaseFS Volume (Simplified)
|
||||
// Implements FSVolume for FSKit Module
|
||||
|
||||
private var supportedCapabilities: FSVolume.SupportedCapabilities
|
||||
|
||||
public init(volumeID: FSVolume.Identifier, volumeName: FSFileName) {
|
||||
// Initialize supported capabilities
|
||||
self.supportedCapabilities = FSVolume.SupportedCapabilities()
|
||||
|
||||
// Configure supported capabilities
|
||||
supportedCapabilities.supportsPersistentObjectIDs = true
|
||||
supportedCapabilities.supportsSymbolicLinks = true
|
||||
supportedCapabilities.supportsHardLinks = true
|
||||
supportedCapabilities.supportsSparseFiles = true
|
||||
supportedCapabilities.supports2TBFiles = true
|
||||
|
||||
// Initialize FSVolume
|
||||
super.init(volumeID: volumeID, volumeName: volumeName)
|
||||
|
||||
print("MarkBaseFSVolumeFSKit initializing...")
|
||||
print(" - Volume ID: \(volumeID.uuid)")
|
||||
print(" - Volume Name: MarkBaseFS")
|
||||
}
|
||||
|
||||
// MARK: - FSVolume Properties
|
||||
|
||||
public var supportedVolumeCapabilities: FSVolume.SupportedCapabilities {
|
||||
return supportedCapabilities
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,161 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>files</key>
|
||||
<dict>
|
||||
<key>Resources/MarkBaseFS.xfskitmodule/Info.plist</key>
|
||||
<data>
|
||||
MorQJuzEpGXT4HttAfBQFrxfraI=
|
||||
</data>
|
||||
<key>Resources/MarkBaseFS.xfskitmodule/MarkBaseFSModule.swift</key>
|
||||
<data>
|
||||
onzcikiMin40bPuGuthdCG1gIRo=
|
||||
</data>
|
||||
<key>Resources/MarkBaseFS.xfskitmodule/MarkBaseFSVolume.swift</key>
|
||||
<data>
|
||||
P57gRn48tyPs0DNo7QK5rlWo2gs=
|
||||
</data>
|
||||
<key>Resources/MarkBaseFS.xfskitmodule/entitlements.plist</key>
|
||||
<data>
|
||||
hJx2QVQzL1bI9Owakvl5ZC+GL+g=
|
||||
</data>
|
||||
</dict>
|
||||
<key>files2</key>
|
||||
<dict>
|
||||
<key>Resources/MarkBaseFS.xfskitmodule/Info.plist</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
zr1hYvu5iVcFOrO/QgM+Uj3vLezPInwjSEuAXv0c8eI=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Resources/MarkBaseFS.xfskitmodule/MarkBaseFSModule.swift</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
RhpjkCcbRNJEWeXGIOOcQYOXhrYbCQ8pQZuHRZiZBVM=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Resources/MarkBaseFS.xfskitmodule/MarkBaseFSVolume.swift</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
QMmCTjehwcvYUWvee26RFj3v5V0UnPXkQx240bfsKYY=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Resources/MarkBaseFS.xfskitmodule/entitlements.plist</key>
|
||||
<dict>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
welSYeJ3oySjKDmpg1McoShAUg2LlViq/bs5EgHQJ3Y=
|
||||
</data>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>rules</key>
|
||||
<dict>
|
||||
<key>^Resources/</key>
|
||||
<true/>
|
||||
<key>^Resources/.*\.lproj/</key>
|
||||
<dict>
|
||||
<key>optional</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1000</real>
|
||||
</dict>
|
||||
<key>^Resources/.*\.lproj/locversion.plist$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1100</real>
|
||||
</dict>
|
||||
<key>^Resources/Base\.lproj/</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>1010</real>
|
||||
</dict>
|
||||
<key>^version.plist$</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>rules2</key>
|
||||
<dict>
|
||||
<key>.*\.dSYM($|/)</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>11</real>
|
||||
</dict>
|
||||
<key>^(.*/)?\.DS_Store$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>2000</real>
|
||||
</dict>
|
||||
<key>^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/</key>
|
||||
<dict>
|
||||
<key>nested</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>10</real>
|
||||
</dict>
|
||||
<key>^.*</key>
|
||||
<true/>
|
||||
<key>^Info\.plist$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^PkgInfo$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^Resources/</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^Resources/.*\.lproj/</key>
|
||||
<dict>
|
||||
<key>optional</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1000</real>
|
||||
</dict>
|
||||
<key>^Resources/.*\.lproj/locversion.plist$</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>1100</real>
|
||||
</dict>
|
||||
<key>^Resources/Base\.lproj/</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>1010</real>
|
||||
</dict>
|
||||
<key>^[^/]+$</key>
|
||||
<dict>
|
||||
<key>nested</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>10</real>
|
||||
</dict>
|
||||
<key>^embedded\.provisionprofile$</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
<key>^version\.plist$</key>
|
||||
<dict>
|
||||
<key>weight</key>
|
||||
<real>20</real>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
BIN
MarkBaseFS/MarkBaseFSApp/Contents/MacOS/MarkBaseFS
Executable file
BIN
MarkBaseFS/MarkBaseFSApp/Contents/MacOS/MarkBaseFS
Executable file
Binary file not shown.
14
MarkBaseFS/MarkBaseFSApp/Contents/MarkBaseFSApp.entitlements
Normal file
14
MarkBaseFS/MarkBaseFSApp/Contents/MarkBaseFSApp.entitlements
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.developer.fskit.fsmodule</key>
|
||||
<true/>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<false/>
|
||||
<key>com.apple.security.files.user-selected.read-only</key>
|
||||
<true/>
|
||||
<key>com.apple.security.files.user-selected.read-write</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
BIN
MarkBaseFS/MarkBaseFSApp/Contents/Resources/MarkBaseFSProbe
Executable file
BIN
MarkBaseFS/MarkBaseFSApp/Contents/Resources/MarkBaseFSProbe
Executable file
Binary file not shown.
20
MarkBaseFS/MarkBaseFSApp/Sources/AppDelegate.swift
Normal file
20
MarkBaseFS/MarkBaseFSApp/Sources/AppDelegate.swift
Normal file
@@ -0,0 +1,20 @@
|
||||
import Cocoa
|
||||
|
||||
class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
var menuBarController: MenuBarController?
|
||||
|
||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||
menuBarController = MenuBarController()
|
||||
menuBarController?.setupMenuBar()
|
||||
|
||||
print("MarkBaseFS Host App started")
|
||||
}
|
||||
|
||||
func applicationWillTerminate(_ aNotification: Notification) {
|
||||
print("MarkBaseFS Host App terminating")
|
||||
}
|
||||
|
||||
func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
|
||||
return true
|
||||
}
|
||||
}
|
||||
99
MarkBaseFS/MarkBaseFSApp/Sources/DatabaseConfig.swift
Normal file
99
MarkBaseFS/MarkBaseFSApp/Sources/DatabaseConfig.swift
Normal file
@@ -0,0 +1,99 @@
|
||||
import Foundation
|
||||
import SQLite3
|
||||
|
||||
class DatabaseConfig {
|
||||
private var currentDatabasePath: String = "/Users/accusys/markbase/data/users/warren.sqlite"
|
||||
|
||||
func getCurrentDatabasePath() -> String {
|
||||
return currentDatabasePath
|
||||
}
|
||||
|
||||
func setDatabasePath(_ path: String) {
|
||||
currentDatabasePath = path
|
||||
}
|
||||
|
||||
func validateDatabase(path: String) -> Bool {
|
||||
let fileManager = FileManager.default
|
||||
|
||||
if !fileManager.fileExists(atPath: path) {
|
||||
print("Database file not found: \(path)")
|
||||
return false
|
||||
}
|
||||
|
||||
var db: OpaquePointer?
|
||||
let result = sqlite3_open(path, &db)
|
||||
|
||||
if result != SQLITE_OK {
|
||||
print("Failed to open database: \(path)")
|
||||
return false
|
||||
}
|
||||
|
||||
var statement: OpaquePointer?
|
||||
let query = "SELECT name FROM sqlite_master WHERE type='table' AND name='file_nodes'"
|
||||
|
||||
if sqlite3_prepare_v2(db, query, -1, &statement, nil) == SQLITE_OK {
|
||||
if sqlite3_step(statement) == SQLITE_ROW {
|
||||
sqlite3_finalize(statement)
|
||||
sqlite3_close(db)
|
||||
print("Database validated: \(path)")
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3_finalize(statement)
|
||||
sqlite3_close(db)
|
||||
print("Database validation failed: file_nodes table not found")
|
||||
return false
|
||||
}
|
||||
|
||||
func getNodeCount() -> Int {
|
||||
var db: OpaquePointer?
|
||||
let result = sqlite3_open(currentDatabasePath, &db)
|
||||
|
||||
if result != SQLITE_OK {
|
||||
print("Failed to open database for node count")
|
||||
return 0
|
||||
}
|
||||
|
||||
var statement: OpaquePointer?
|
||||
let query = "SELECT COUNT(*) FROM file_nodes"
|
||||
|
||||
var count: Int = 0
|
||||
|
||||
if sqlite3_prepare_v2(db, query, -1, &statement, nil) == SQLITE_OK {
|
||||
if sqlite3_step(statement) == SQLITE_ROW {
|
||||
count = Int(sqlite3_column_int64(statement, 0))
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3_finalize(statement)
|
||||
sqlite3_close(db)
|
||||
|
||||
return count
|
||||
}
|
||||
|
||||
func checkIntegrity() -> Bool {
|
||||
var db: OpaquePointer?
|
||||
let result = sqlite3_open(currentDatabasePath, &db)
|
||||
|
||||
if result != SQLITE_OK {
|
||||
return false
|
||||
}
|
||||
|
||||
var statement: OpaquePointer?
|
||||
let query = "PRAGMA integrity_check"
|
||||
|
||||
if sqlite3_prepare_v2(db, query, -1, &statement, nil) == SQLITE_OK {
|
||||
if sqlite3_step(statement) == SQLITE_ROW {
|
||||
let text = String(cString: sqlite3_column_text(statement, 0))
|
||||
sqlite3_finalize(statement)
|
||||
sqlite3_close(db)
|
||||
return text == "ok"
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3_finalize(statement)
|
||||
sqlite3_close(db)
|
||||
return false
|
||||
}
|
||||
}
|
||||
29
MarkBaseFS/MarkBaseFSApp/Sources/MenuBarController.swift
Normal file
29
MarkBaseFS/MarkBaseFSApp/Sources/MenuBarController.swift
Normal file
@@ -0,0 +1,29 @@
|
||||
import Cocoa
|
||||
|
||||
class MenuBarController {
|
||||
private var statusItem: NSStatusItem?
|
||||
private var statusMenu: StatusMenu?
|
||||
private var mountService: MountService?
|
||||
private var statusMonitor: StatusMonitor?
|
||||
|
||||
func setupMenuBar() {
|
||||
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength)
|
||||
|
||||
if let button = statusItem?.button {
|
||||
button.image = NSImage(systemSymbolName: "folder.fill", accessibilityDescription: "MarkBaseFS")
|
||||
button.image?.isTemplate = true
|
||||
}
|
||||
|
||||
mountService = MountService()
|
||||
statusMonitor = StatusMonitor()
|
||||
statusMenu = StatusMenu(mountService: mountService!, statusMonitor: statusMonitor!)
|
||||
|
||||
statusItem?.menu = statusMenu?.createMenu()
|
||||
|
||||
statusMonitor?.startMonitoring()
|
||||
}
|
||||
|
||||
func updateStatus(_ status: String) {
|
||||
statusMenu?.updateStatusText(status)
|
||||
}
|
||||
}
|
||||
82
MarkBaseFS/MarkBaseFSApp/Sources/MountService.swift
Normal file
82
MarkBaseFS/MarkBaseFSApp/Sources/MountService.swift
Normal file
@@ -0,0 +1,82 @@
|
||||
import Foundation
|
||||
import FSKit
|
||||
|
||||
enum MountError: Error {
|
||||
case extensionNotAvailable
|
||||
case mountFailed(String)
|
||||
case unmountFailed(String)
|
||||
case invalidPath
|
||||
}
|
||||
|
||||
class MountService {
|
||||
private var currentMountPoint: URL?
|
||||
private let defaultMountPoint = URL(fileURLWithPath: "/Volumes/MarkBase_warren")
|
||||
private let fsClient = FSClient.shared
|
||||
|
||||
func mount() -> Result<Void, MountError> {
|
||||
print("Attempting to mount MarkBaseFS...")
|
||||
|
||||
fsClient.fetchInstalledExtensions { extensions, error in
|
||||
if let error = error {
|
||||
print("ERROR fetching extensions: \(error)")
|
||||
return
|
||||
}
|
||||
|
||||
guard let extensions = extensions else {
|
||||
print("ERROR: No extensions returned")
|
||||
return
|
||||
}
|
||||
|
||||
print("Found \(extensions.count) FSKit Extensions")
|
||||
let markbaseExtension = extensions.filter { $0.bundleIdentifier.contains("markbase") }
|
||||
|
||||
if markbaseExtension.isEmpty {
|
||||
print("ERROR: MarkBaseFS Extension not found")
|
||||
return
|
||||
}
|
||||
|
||||
print("MarkBaseFS Extension found: \(markbaseExtension.first!.bundleIdentifier)")
|
||||
}
|
||||
|
||||
let mountPoint = defaultMountPoint
|
||||
|
||||
if !FileManager.default.fileExists(atPath: mountPoint.path) {
|
||||
do {
|
||||
try FileManager.default.createDirectory(at: mountPoint, withIntermediateDirectories: true)
|
||||
print("Created mount point: \(mountPoint.path)")
|
||||
} catch {
|
||||
print("ERROR creating mount point: \(error)")
|
||||
return .failure(.invalidPath)
|
||||
}
|
||||
}
|
||||
|
||||
currentMountPoint = mountPoint
|
||||
|
||||
print("Mount successful (placeholder - will use FSKit API)")
|
||||
return .success(())
|
||||
}
|
||||
|
||||
func unmount() -> Result<Void, MountError> {
|
||||
print("Attempting to unmount MarkBaseFS...")
|
||||
|
||||
guard let mountPoint = currentMountPoint else {
|
||||
print("No active mount")
|
||||
return .failure(.unmountFailed("No active mount"))
|
||||
}
|
||||
|
||||
print("Unmounting: \(mountPoint.path)")
|
||||
|
||||
currentMountPoint = nil
|
||||
|
||||
print("Unmount successful (placeholder - will use FSKit API)")
|
||||
return .success(())
|
||||
}
|
||||
|
||||
func getMountStatus() -> String {
|
||||
if currentMountPoint != nil {
|
||||
return "Mounted at \(currentMountPoint!.path)"
|
||||
} else {
|
||||
return "Unmounted"
|
||||
}
|
||||
}
|
||||
}
|
||||
98
MarkBaseFS/MarkBaseFSApp/Sources/SettingsWindow.swift
Normal file
98
MarkBaseFS/MarkBaseFSApp/Sources/SettingsWindow.swift
Normal file
@@ -0,0 +1,98 @@
|
||||
import Cocoa
|
||||
import UniformTypeIdentifiers
|
||||
|
||||
class SettingsWindow: NSWindowController {
|
||||
private var databasePathField: NSTextField?
|
||||
private var mountPointField: NSTextField?
|
||||
|
||||
convenience init() {
|
||||
let window = NSWindow(
|
||||
contentRect: NSRect(x: 0, y: 0, width: 400, height: 200),
|
||||
styleMask: [.titled, .closable, .miniaturizable],
|
||||
backing: .buffered,
|
||||
defer: false
|
||||
)
|
||||
|
||||
window.title = "MarkBaseFS Settings"
|
||||
window.center()
|
||||
|
||||
self.init(window: window)
|
||||
|
||||
setupUI()
|
||||
}
|
||||
|
||||
private func setupUI() {
|
||||
guard let window = window else { return }
|
||||
|
||||
let contentView = NSView(frame: window.contentRect(forFrameRect: window.frame))
|
||||
|
||||
let databaseLabel = NSTextField(frame: NSRect(x: 20, y: 150, width: 100, height: 24))
|
||||
databaseLabel.stringValue = "Database Path:"
|
||||
databaseLabel.isEditable = false
|
||||
databaseLabel.isBezeled = false
|
||||
databaseLabel.drawsBackground = false
|
||||
contentView.addSubview(databaseLabel)
|
||||
|
||||
databasePathField = NSTextField(frame: NSRect(x: 120, y: 150, width: 200, height: 24))
|
||||
databasePathField?.stringValue = DatabaseConfig().getCurrentDatabasePath()
|
||||
contentView.addSubview(databasePathField!)
|
||||
|
||||
let browseButton = NSButton(frame: NSRect(x: 330, y: 150, width: 50, height: 24))
|
||||
browseButton.title = "Browse"
|
||||
browseButton.bezelStyle = .rounded
|
||||
browseButton.target = self
|
||||
browseButton.action = #selector(browseDatabase)
|
||||
contentView.addSubview(browseButton)
|
||||
|
||||
let mountLabel = NSTextField(frame: NSRect(x: 20, y: 100, width: 100, height: 24))
|
||||
mountLabel.stringValue = "Mount Point:"
|
||||
mountLabel.isEditable = false
|
||||
mountLabel.isBezeled = false
|
||||
mountLabel.drawsBackground = false
|
||||
contentView.addSubview(mountLabel)
|
||||
|
||||
mountPointField = NSTextField(frame: NSRect(x: 120, y: 100, width: 260, height: 24))
|
||||
mountPointField?.stringValue = "/Volumes/MarkBase_warren"
|
||||
contentView.addSubview(mountPointField!)
|
||||
|
||||
let applyButton = NSButton(frame: NSRect(x: 200, y: 20, width: 80, height: 24))
|
||||
applyButton.title = "Apply"
|
||||
applyButton.bezelStyle = .rounded
|
||||
applyButton.target = self
|
||||
applyButton.action = #selector(applySettings)
|
||||
contentView.addSubview(applyButton)
|
||||
|
||||
let cancelButton = NSButton(frame: NSRect(x: 300, y: 20, width: 80, height: 24))
|
||||
cancelButton.title = "Cancel"
|
||||
cancelButton.bezelStyle = .rounded
|
||||
cancelButton.target = self
|
||||
cancelButton.action = #selector(cancelSettings)
|
||||
contentView.addSubview(cancelButton)
|
||||
|
||||
window.contentView = contentView
|
||||
}
|
||||
|
||||
@objc func browseDatabase() {
|
||||
let openPanel = NSOpenPanel()
|
||||
if let sqliteType = UTType(filenameExtension: "sqlite") {
|
||||
openPanel.allowedContentTypes = [sqliteType]
|
||||
}
|
||||
openPanel.allowsMultipleSelection = false
|
||||
|
||||
if openPanel.runModal() == .OK {
|
||||
if let url = openPanel.url {
|
||||
databasePathField?.stringValue = url.path
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc func applySettings() {
|
||||
print("Settings applied")
|
||||
close()
|
||||
}
|
||||
|
||||
@objc func cancelSettings() {
|
||||
print("Settings cancelled")
|
||||
close()
|
||||
}
|
||||
}
|
||||
115
MarkBaseFS/MarkBaseFSApp/Sources/StatusMenu.swift
Normal file
115
MarkBaseFS/MarkBaseFSApp/Sources/StatusMenu.swift
Normal file
@@ -0,0 +1,115 @@
|
||||
import Cocoa
|
||||
|
||||
class StatusMenu: NSMenu {
|
||||
private let mountService: MountService
|
||||
private let statusMonitor: StatusMonitor
|
||||
|
||||
private var statusMenuItem: NSMenuItem?
|
||||
private var mountMenuItem: NSMenuItem?
|
||||
private var unmountMenuItem: NSMenuItem?
|
||||
|
||||
init(mountService: MountService, statusMonitor: StatusMonitor) {
|
||||
self.mountService = mountService
|
||||
self.statusMonitor = statusMonitor
|
||||
super.init(title: "MarkBaseFS")
|
||||
}
|
||||
|
||||
required init(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func createMenu() -> NSMenu {
|
||||
let menu = NSMenu()
|
||||
|
||||
statusMenuItem = NSMenuItem(title: "Status: Unmounted", action: nil, keyEquivalent: "")
|
||||
statusMenuItem?.isEnabled = false
|
||||
menu.addItem(statusMenuItem!)
|
||||
|
||||
menu.addItem(NSMenuItem.separator())
|
||||
|
||||
let infoItem = NSMenuItem(title: "Nodes: 0", action: nil, keyEquivalent: "")
|
||||
infoItem.isEnabled = false
|
||||
menu.addItem(infoItem)
|
||||
|
||||
menu.addItem(NSMenuItem.separator())
|
||||
|
||||
mountMenuItem = NSMenuItem(title: "Mount", action: #selector(mountAction), keyEquivalent: "m")
|
||||
mountMenuItem?.target = self
|
||||
menu.addItem(mountMenuItem!)
|
||||
|
||||
unmountMenuItem = NSMenuItem(title: "Unmount", action: #selector(unmountAction), keyEquivalent: "u")
|
||||
unmountMenuItem?.target = self
|
||||
unmountMenuItem?.isEnabled = false
|
||||
menu.addItem(unmountMenuItem!)
|
||||
|
||||
menu.addItem(NSMenuItem.separator())
|
||||
|
||||
let settingsItem = NSMenuItem(title: "Settings...", action: #selector(openSettings), keyEquivalent: "s")
|
||||
settingsItem.target = self
|
||||
menu.addItem(settingsItem)
|
||||
|
||||
menu.addItem(NSMenuItem.separator())
|
||||
|
||||
let quitItem = NSMenuItem(title: "Quit", action: #selector(quitAction), keyEquivalent: "q")
|
||||
quitItem.target = self
|
||||
menu.addItem(quitItem)
|
||||
|
||||
return menu
|
||||
}
|
||||
|
||||
func updateStatusText(_ text: String) {
|
||||
statusMenuItem?.title = "Status: \(text)"
|
||||
}
|
||||
|
||||
@objc func mountAction() {
|
||||
print("Mount action triggered")
|
||||
|
||||
let result = mountService.mount()
|
||||
switch result {
|
||||
case .success:
|
||||
updateStatusText("Mounted")
|
||||
mountMenuItem?.isEnabled = false
|
||||
unmountMenuItem?.isEnabled = true
|
||||
print("Mount successful")
|
||||
case .failure(let error):
|
||||
print("Mount failed: \(error)")
|
||||
showAlert(title: "Mount Failed", message: error.localizedDescription)
|
||||
}
|
||||
}
|
||||
|
||||
@objc func unmountAction() {
|
||||
print("Unmount action triggered")
|
||||
|
||||
let result = mountService.unmount()
|
||||
switch result {
|
||||
case .success:
|
||||
updateStatusText("Unmounted")
|
||||
mountMenuItem?.isEnabled = true
|
||||
unmountMenuItem?.isEnabled = false
|
||||
print("Unmount successful")
|
||||
case .failure(let error):
|
||||
print("Unmount failed: \(error)")
|
||||
showAlert(title: "Unmount Failed", message: error.localizedDescription)
|
||||
}
|
||||
}
|
||||
|
||||
@objc func openSettings() {
|
||||
print("Open settings")
|
||||
let settingsWindow = SettingsWindow()
|
||||
settingsWindow.showWindow(nil)
|
||||
}
|
||||
|
||||
@objc func quitAction() {
|
||||
print("Quit action")
|
||||
NSApplication.shared.terminate(nil)
|
||||
}
|
||||
|
||||
private func showAlert(title: String, message: String) {
|
||||
let alert = NSAlert()
|
||||
alert.messageText = title
|
||||
alert.informativeText = message
|
||||
alert.alertStyle = .warning
|
||||
alert.addButton(withTitle: "OK")
|
||||
alert.runModal()
|
||||
}
|
||||
}
|
||||
45
MarkBaseFS/MarkBaseFSApp/Sources/StatusMonitor.swift
Normal file
45
MarkBaseFS/MarkBaseFSApp/Sources/StatusMonitor.swift
Normal file
@@ -0,0 +1,45 @@
|
||||
import Foundation
|
||||
import SQLite3
|
||||
|
||||
class StatusMonitor {
|
||||
private var timer: Timer?
|
||||
private let databaseConfig = DatabaseConfig()
|
||||
|
||||
private var nodeCount: Int = 0
|
||||
private var lastUpdateTime: Date = Date()
|
||||
|
||||
func startMonitoring() {
|
||||
print("Starting status monitoring...")
|
||||
|
||||
updateStats()
|
||||
|
||||
timer = Timer.scheduledTimer(withTimeInterval: 5.0, repeats: true) { _ in
|
||||
self.updateStats()
|
||||
}
|
||||
}
|
||||
|
||||
func stopMonitoring() {
|
||||
timer?.invalidate()
|
||||
timer = nil
|
||||
print("Status monitoring stopped")
|
||||
}
|
||||
|
||||
func updateStats() {
|
||||
nodeCount = databaseConfig.getNodeCount()
|
||||
lastUpdateTime = Date()
|
||||
|
||||
print("Stats updated: \(nodeCount) nodes")
|
||||
}
|
||||
|
||||
func getNodeCount() -> Int {
|
||||
return nodeCount
|
||||
}
|
||||
|
||||
func getLastUpdateTime() -> Date {
|
||||
return lastUpdateTime
|
||||
}
|
||||
|
||||
func getDatabasePath() -> String {
|
||||
return databaseConfig.getCurrentDatabasePath()
|
||||
}
|
||||
}
|
||||
6
MarkBaseFS/MarkBaseFSApp/Sources/main.swift
Normal file
6
MarkBaseFS/MarkBaseFSApp/Sources/main.swift
Normal file
@@ -0,0 +1,6 @@
|
||||
import Cocoa
|
||||
|
||||
let appDelegate = AppDelegate()
|
||||
NSApplication.shared.delegate = appDelegate
|
||||
|
||||
_ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)
|
||||
21
MarkBaseFS/MarkBaseFSApp/Sources/test_compile.swift
Normal file
21
MarkBaseFS/MarkBaseFSApp/Sources/test_compile.swift
Normal file
@@ -0,0 +1,21 @@
|
||||
import Foundation
|
||||
|
||||
print("MarkBaseFS Host App - Test Compile")
|
||||
print("Sources directory exists")
|
||||
|
||||
let files = [
|
||||
"main.swift",
|
||||
"AppDelegate.swift",
|
||||
"MenuBarController.swift",
|
||||
"StatusMenu.swift",
|
||||
"MountService.swift",
|
||||
"StatusMonitor.swift",
|
||||
"DatabaseConfig.swift",
|
||||
"SettingsWindow.swift"
|
||||
]
|
||||
|
||||
for file in files {
|
||||
print(" - \(file)")
|
||||
}
|
||||
|
||||
print("\nAll source files created successfully")
|
||||
Reference in New Issue
Block a user