mirror of
https://github.com/samsonjs/vibetunnel.git
synced 2026-04-13 12:35:54 +00:00
Add Xcode build phase for web frontend
- Added "Build Web Frontend" phase that runs npm install and npm run build - Copies built web assets from web/public/ to app Resources folder - Modified TunnelServer.swift to only serve static files from bundled Resources - Removed development path options from static file serving - Added proper PATH configuration for npm in build script
This commit is contained in:
parent
3a6665c13f
commit
91f12f6ed6
2 changed files with 45 additions and 45 deletions
|
|
@ -125,6 +125,7 @@
|
|||
788687ED2DFF4FCB00B22C15 /* Sources */,
|
||||
788687EE2DFF4FCB00B22C15 /* Frameworks */,
|
||||
788687EF2DFF4FCB00B22C15 /* Resources */,
|
||||
B2C3D4E5F6A7B8C9D0E1F234 /* Build Web Frontend */,
|
||||
A189466CB0AD49BEBE16B954 /* Build tty-fwd Universal Binary */,
|
||||
);
|
||||
buildRules = (
|
||||
|
|
@ -262,6 +263,31 @@
|
|||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
B2C3D4E5F6A7B8C9D0E1F234 /* Build Web Frontend */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
"$(SRCROOT)/web/package.json",
|
||||
"$(SRCROOT)/web/src",
|
||||
"$(SRCROOT)/web/tsconfig.json",
|
||||
"$(SRCROOT)/web/tsconfig.client.json",
|
||||
"$(SRCROOT)/web/tailwind.config.js",
|
||||
);
|
||||
name = "Build Web Frontend";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
"$(BUILT_PRODUCTS_DIR)/$(CONTENTS_FOLDER_PATH)/Resources/web/public",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "# Build web frontend\necho \"Building web frontend...\"\n\n# Setup PATH to include common Node.js installation locations\nexport PATH=\"/usr/local/bin:/opt/homebrew/bin:/opt/homebrew/sbin:$HOME/.nvm/versions/node/$(ls -1 $HOME/.nvm/versions/node 2>/dev/null | tail -1)/bin:$PATH\"\n\n# Get the project directory\nPROJECT_DIR=\"${SRCROOT}\"\nWEB_DIR=\"${PROJECT_DIR}/web\"\nPUBLIC_DIR=\"${WEB_DIR}/public\"\nDEST_DIR=\"${BUILT_PRODUCTS_DIR}/${CONTENTS_FOLDER_PATH}/Resources/web/public\"\n\n# Export CI environment variable to prevent interactive prompts\nexport CI=true\n\n# Check if npm is available\nif ! command -v npm &> /dev/null; then\n echo \"error: npm could not be found in PATH\"\n echo \"PATH is: $PATH\"\n echo \"Please ensure Node.js is installed\"\n exit 1\nfi\n\n# Print npm version for debugging\necho \"Using npm version: $(npm --version)\"\necho \"Using node version: $(node --version)\"\n\n# Check if web directory exists\nif [ ! -d \"${WEB_DIR}\" ]; then\n echo \"error: Web directory not found at ${WEB_DIR}\"\n exit 1\nfi\n\n# Change to web directory\ncd \"${WEB_DIR}\"\n\n# Install dependencies if node_modules doesn't exist or package-lock.json is newer\nif [ ! -d \"node_modules\" ] || [ \"package-lock.json\" -nt \"node_modules\" ]; then\n echo \"Installing npm dependencies...\"\n npm install --no-progress --no-audit\n if [ $? -ne 0 ]; then\n echo \"error: npm install failed\"\n exit 1\n fi\nfi\n\n# Build the web frontend\necho \"Running npm build...\"\nnpm run build\nif [ $? -ne 0 ]; then\n echo \"error: npm run build failed\"\n exit 1\nfi\n\n# Create destination directory\nmkdir -p \"${DEST_DIR}\"\n\n# Copy built files to Resources\necho \"Copying web files to app bundle...\"\nif [ -d \"${PUBLIC_DIR}\" ]; then\n # Copy all files from public directory\n cp -R \"${PUBLIC_DIR}/\"* \"${DEST_DIR}/\"\n echo \"Web frontend files copied to ${DEST_DIR}\"\nelse\n echo \"error: Public directory not found at ${PUBLIC_DIR}\"\n exit 1\nfi\n";
|
||||
};
|
||||
A189466CB0AD49BEBE16B954 /* Build tty-fwd Universal Binary */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
|
|
|
|||
|
|
@ -474,54 +474,28 @@ public final class TunnelServer {
|
|||
// MARK: - Static File Serving
|
||||
|
||||
private func serveStaticFile(path: String) async -> Response {
|
||||
// Try multiple possible paths for the web/public directory
|
||||
let possiblePaths = [
|
||||
// Bundle resource path (for production app)
|
||||
Bundle.main.resourcePath?.appending("/web/public"),
|
||||
// Current working directory (for development)
|
||||
FileManager.default.currentDirectoryPath + "/web/public",
|
||||
// Project directory (if running from source)
|
||||
"/Users/mitsuhiko/Development/vibetunnel/web/public",
|
||||
// Relative to bundle path
|
||||
Bundle.main.bundlePath + "/../../../web/public"
|
||||
].compactMap(\.self)
|
||||
|
||||
// Serve files only from the bundled Resources folder
|
||||
guard let resourcePath = Bundle.main.resourcePath else {
|
||||
logger.error("Bundle resource path not found")
|
||||
return errorResponse(message: "Resource bundle not available", status: .internalServerError)
|
||||
}
|
||||
|
||||
let webPublicPath = resourcePath + "/web/public"
|
||||
|
||||
// Sanitize path to prevent directory traversal attacks
|
||||
let sanitizedPath = path.replacingOccurrences(of: "..", with: "")
|
||||
|
||||
var webPublicPath: String?
|
||||
var fullPath: String?
|
||||
|
||||
// Find the first path that exists
|
||||
for testPath in possiblePaths {
|
||||
let testFullPath = testPath + "/" + sanitizedPath
|
||||
if FileManager.default.fileExists(atPath: testFullPath) {
|
||||
webPublicPath = testPath
|
||||
fullPath = testFullPath
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// If no file found, try just checking directory existence
|
||||
if fullPath == nil {
|
||||
for testPath in possiblePaths {
|
||||
if FileManager.default.fileExists(atPath: testPath, isDirectory: nil) {
|
||||
webPublicPath = testPath
|
||||
fullPath = testPath + "/" + sanitizedPath
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
guard let finalPath = fullPath, webPublicPath != nil else {
|
||||
logger.error("Could not find web/public directory in any of these paths:")
|
||||
for testPath in possiblePaths {
|
||||
logger.error(" - \(testPath)")
|
||||
}
|
||||
return errorResponse(message: "Web directory not found", status: .notFound)
|
||||
let fullPath = webPublicPath + "/" + sanitizedPath
|
||||
|
||||
// Check if the web directory exists in Resources
|
||||
var isWebDirExists: ObjCBool = false
|
||||
if !FileManager.default.fileExists(atPath: webPublicPath, isDirectory: &isWebDirExists) || !isWebDirExists.boolValue {
|
||||
logger.error("Web resources not found at: \(webPublicPath)")
|
||||
logger.error("Make sure the app was built with the 'Build Web Frontend' phase")
|
||||
return errorResponse(message: "Web resources not bundled", status: .internalServerError)
|
||||
}
|
||||
|
||||
var isDirectory: ObjCBool = false
|
||||
guard FileManager.default.fileExists(atPath: finalPath, isDirectory: &isDirectory) else {
|
||||
guard FileManager.default.fileExists(atPath: fullPath, isDirectory: &isDirectory) else {
|
||||
return errorResponse(message: "File not found", status: .notFound)
|
||||
}
|
||||
|
||||
|
|
@ -531,7 +505,7 @@ public final class TunnelServer {
|
|||
}
|
||||
|
||||
do {
|
||||
let fileData = try Data(contentsOf: URL(fileURLWithPath: finalPath))
|
||||
let fileData = try Data(contentsOf: URL(fileURLWithPath: fullPath))
|
||||
var buffer = ByteBuffer()
|
||||
buffer.writeBytes(fileData)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue