Skip to content

Commit 724954e

Browse files
committed
perf: Create a centralized url builder and remove double/inconsistent encoding
1 parent 2e753ae commit 724954e

File tree

6 files changed

+99
-61
lines changed

6 files changed

+99
-61
lines changed

FileBrowserClient/FileBrowserClient/FileDetailView.swift

Lines changed: 36 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -354,16 +354,21 @@ struct FileDetailView: View {
354354

355355
func renameFile() {
356356
let fromPath = file.path
357-
let toPath = URL(fileURLWithPath: file.path).deletingLastPathComponent().appendingPathComponent(newName).path
358-
359-
guard let encodedFrom = fromPath.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed),
360-
let encodedTo = toPath.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {
361-
self.error = "Failed to encode rename paths"
362-
return
363-
}
364-
365-
let urlString = "\(serverURL)/api/resources/\(removePrefix(urlPath: encodedFrom))?action=rename&destination=/\(removePrefix(urlPath: encodedTo))&override=false&rename=false"
366-
guard let url = URL(string: urlString) else {
357+
let toPath = URL(fileURLWithPath: file.path)
358+
.deletingLastPathComponent()
359+
.appendingPathComponent(newName)
360+
.path
361+
362+
guard let url = buildAPIURL(
363+
base: serverURL,
364+
pathComponents: ["api", "resources", fromPath],
365+
queryItems: [
366+
URLQueryItem(name: "action", value: "rename"),
367+
URLQueryItem(name: "destination", value: "/" + toPath),
368+
URLQueryItem(name: "override", value: "false"),
369+
URLQueryItem(name: "rename", value: "false")
370+
]
371+
) else {
367372
self.error = "Invalid rename URL"
368373
return
369374
}
@@ -460,13 +465,13 @@ struct FileDetailView: View {
460465
return
461466
}
462467

463-
guard let encodedPath = file.path.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) else {
464-
self.error = "Failed to encode path for preview"
465-
return
466-
}
467-
468-
let urlString = "\(serverURL)/api/preview/big/\(removePrefix(urlPath: encodedPath))?auth=\(token)"
469-
guard let url = URL(string: urlString) else {
468+
guard let url = buildAPIURL(
469+
base: serverURL,
470+
pathComponents: ["api", "preview", "big", file.path],
471+
queryItems: [
472+
URLQueryItem(name: "auth", value: token)
473+
]
474+
) else {
470475
self.error = "Invalid preview URL"
471476
return
472477
}
@@ -496,13 +501,13 @@ struct FileDetailView: View {
496501
}
497502

498503
func fetchMetadata() {
499-
guard let encodedPath = file.path.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) else {
500-
self.error = "Failed to encode path for metadata"
501-
return
502-
}
503-
504-
let urlString = "\(serverURL)/api/resources/\(removePrefix(urlPath: encodedPath))?view=info"
505-
guard let url = URL(string: urlString) else {
504+
guard let url = buildAPIURL(
505+
base: serverURL,
506+
pathComponents: ["api", "resources", file.path],
507+
queryItems: [
508+
URLQueryItem(name: "view", value: "info")
509+
]
510+
) else {
506511
self.error = "Invalid metadata URL"
507512
return
508513
}
@@ -547,14 +552,13 @@ struct FileDetailView: View {
547552
return
548553
}
549554

550-
guard let encodedPath = file.path.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) else {
551-
self.error = "Failed to encode path for raw download"
552-
isDownloading = false
553-
return
554-
}
555-
556-
let urlString = "\(serverURL)/api/raw/\(removePrefix(urlPath: encodedPath))?auth=\(token)"
557-
guard let url = URL(string: urlString) else {
555+
guard let url = buildAPIURL(
556+
base: serverURL,
557+
pathComponents: ["api", "raw", file.path],
558+
queryItems: [
559+
URLQueryItem(name: "auth", value: token)
560+
]
561+
) else {
558562
self.error = "Invalid raw URL"
559563
isDownloading = false
560564
return

FileBrowserClient/FileBrowserClient/FileListView.swift

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -716,33 +716,31 @@ struct FileListView: View {
716716
finalQuery = "\(prefix) \(finalQuery)"
717717
}
718718

719-
guard let encodedQuery = finalQuery.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {
720-
DispatchQueue.main.async {
721-
errorMessage = "Invalid search: \(query)"
722-
searchInProgress = false
723-
Log.error("❌ Search init query encoding failed")
724-
}
725-
return nil
726-
}
727-
728719
let searchLocation: String
729720
if pathStack.isEmpty || currentPath == "/" {
730-
searchLocation = "/"
721+
// No extra path component, search at /api/search
722+
searchLocation = ""
731723
statusMessage = StatusPayload(
732724
text: "⚠️ Searching from the home page may take longer and be inaccurate",
733725
color: .yellow,
734726
duration: 3.5
735727
)
736728
} else {
737-
guard let encodedPath = currentPath.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {
738-
errorMessage = "Failed to search from \(currentPath)"
739-
Log.error("❌ Search final query encoding failed at \(currentPath)")
740-
return nil
741-
}
742-
searchLocation = encodedPath
729+
searchLocation = currentPath.trimmingCharacters(in: CharacterSet(charactersIn: "/"))
730+
}
731+
732+
var pathComponents = ["api", "search"]
733+
if !searchLocation.isEmpty {
734+
pathComponents.append(searchLocation)
743735
}
744736

745-
return URL(string: "\(serverURL)/api/search/\(removePrefix(urlPath: searchLocation))?query=\(encodedQuery)")
737+
return buildAPIURL(
738+
base: serverURL,
739+
pathComponents: pathComponents,
740+
queryItems: [
741+
URLQueryItem(name: "query", value: finalQuery) // pass raw query string
742+
]
743+
)
746744
}
747745

748746
func searchFiles(query: String) async {
@@ -1143,9 +1141,17 @@ struct FileListView: View {
11431141

11441142
func getUploadURL(serverURL: String, encodedName: String) -> URL? {
11451143
if currentPath == "/" {
1146-
return URL(string: "\(serverURL)/api/tus/\(removePrefix(urlPath: encodedName))?override=false")
1144+
return buildAPIURL(
1145+
base: serverURL,
1146+
pathComponents: ["api", "tus", encodedName],
1147+
queryItems: [URLQueryItem(name: "override", value: "false")]
1148+
)
11471149
}
1148-
return URL(string: "\(serverURL)/api/tus/\(removePrefix(urlPath: currentPath))/\(encodedName)?override=false")
1150+
return buildAPIURL(
1151+
base: serverURL,
1152+
pathComponents: ["api", "tus", currentPath, encodedName],
1153+
queryItems: [URLQueryItem(name: "override", value: "false")]
1154+
)
11491155
}
11501156

11511157
func initiateTusUpload(for fileURL: URL) {
@@ -1156,8 +1162,7 @@ struct FileListView: View {
11561162
}
11571163

11581164
let fileName = fileURL.lastPathComponent
1159-
guard let encodedName = fileName.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed),
1160-
let uploadURL = getUploadURL(serverURL: serverURL, encodedName: encodedName) else {
1165+
guard let uploadURL = getUploadURL(serverURL: serverURL, encodedName: fileName) else {
11611166
Log.error("❌ Invalid upload URL")
11621167
errorTitle = "Invalid upload URL"
11631168
errorMessage = "Failed to construct upload URL for: \(fileName)"
@@ -1493,8 +1498,11 @@ struct FileListView: View {
14931498

14941499
let group = DispatchGroup()
14951500
for item in selectedItems {
1496-
guard let encodedPath = item.path.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed),
1497-
let url = URL(string: "\(baseURL)/api/resources/\(removePrefix(urlPath: encodedPath))") else {
1501+
guard let url = buildAPIURL(
1502+
base: baseURL,
1503+
pathComponents: ["api", "resources", item.path],
1504+
queryItems: []
1505+
) else {
14981506
Log.error("❌ Invalid path for \(item.name)")
14991507
viewModel.errorMessage = "Invalid path for \(item.name)"
15001508
continue
@@ -1591,6 +1599,7 @@ struct FileListView: View {
15911599
return
15921600
}
15931601

1602+
// MARK: Condition based path seaparator
15941603
let separator = isDirectory ? "/?" : "?"
15951604
let resourceType = isDirectory ? "Folder" : "File"
15961605
let emoji = isDirectory ? "📁" : "📄"

FileBrowserClient/FileBrowserClient/FileListViewModel.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,11 @@ class FileListViewModel: ObservableObject {
4545
return
4646
}
4747

48-
guard let encodedPath = path.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed),
49-
let url = URL(string: "\(serverURL)/api/resources/\(removePrefix(urlPath: encodedPath))") else {
48+
guard let url = buildAPIURL(
49+
base: serverURL,
50+
pathComponents: ["api", "resources", path],
51+
queryItems: []
52+
) else {
5053
errorMessage = "Invalid URL"
5154
return
5255
}

FileBrowserClient/FileBrowserClient/MediaPlayerView.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,11 @@ struct MediaPlayerView: View {
4141
}
4242

4343
func loadPlayer() {
44-
let path = file.path.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) ?? ""
45-
if let url = URL(string: "\(serverURL)/api/raw/\(removePrefix(urlPath: path))?auth=\(token)") {
44+
if let url = buildAPIURL(
45+
base: serverURL,
46+
pathComponents: ["api", "raw", file.path],
47+
queryItems: [ URLQueryItem(name: "auth", value: token) ]
48+
) {
4649
DispatchQueue.global(qos: .userInitiated).async {
4750
let asset = AVURLAsset(url: url)
4851
let item = AVPlayerItem(asset: asset)

FileBrowserClient/FileBrowserClient/RemoteThumbnail.swift

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,14 @@ struct RemoteThumbnail: View {
9797
}
9898

9999
// Step 2: Build URL
100-
guard let encodedPath = file.path.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed),
101-
let url = URL(string: "\(serverURL)/api/preview/thumb/\(removePrefix(urlPath: encodedPath))?auth=\(token)&inline=true") else {
100+
guard let url = buildAPIURL(
101+
base: serverURL,
102+
pathComponents: ["api", "preview", "thumb", file.path],
103+
queryItems: [
104+
URLQueryItem(name: "auth", value: token),
105+
URLQueryItem(name: "inline", value: "true")
106+
]
107+
) else {
102108
resetLoadingFiles()
103109
return
104110
}

FileBrowserClient/FileBrowserClient/Utils.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,3 +346,16 @@ func urlPath(_ url: URL) -> String {
346346
}
347347
return result
348348
}
349+
350+
func buildAPIURL(base: String, pathComponents: [String], queryItems: [URLQueryItem]) -> URL? {
351+
guard var url = URL(string: base) else { return nil }
352+
353+
for component in pathComponents {
354+
url = url.appendingPathComponent(component.trimmingCharacters(in: CharacterSet(charactersIn: "/")))
355+
}
356+
357+
var components = URLComponents(url: url, resolvingAgainstBaseURL: false)
358+
components?.queryItems = queryItems
359+
360+
return components?.url
361+
}

0 commit comments

Comments
 (0)