Fixed the root URI paths redirection in the DocCMiddleware type in the library target and also, improved its documentation.

This commit is contained in:
2025-09-27 01:32:26 +02:00
parent a5305e3e6f
commit e6b12a2694
@@ -20,6 +20,28 @@ import struct Hummingbird.Response
import struct Logging.Logger import struct Logging.Logger
/// A middleware that proxies requests to `DocC` documentation containers within a hosting app. /// A middleware that proxies requests to `DocC` documentation containers within a hosting app.
///
/// This middleware routes the contents of a `DocC` documentation container, defined by its resource URI paths, following these rules:
///
/// 1. *Redirects the URI path `/<ArchiveName>` to the path `/<ArchiveName>/`*;
/// 2. *Redirects the URI path `/<ArchiveName>/` to the path `/<ArchiveName>/documentation`*
/// 3. *Redirects the URI path `/<ArchiveName>/documentation` to the path `/<ArchiveName>/documentation/`*
/// 4. *Redirects the URI path `/<ArchiveName>/tutorials` to the path `/<ArchiveName>/tutorials/`*
/// 5. *Redirects the URI path `/<ArchiveName>/documentation/` to the resource on `/<ArchiveName>.doccarchive/documentation/<ArchiveReference>/index.html`*
/// 6. *Redirects the URI path `/<ArchiveName>/tutorials/` to the resource on `/<ArchiveName>.doccarchive/tutorials/<ArchiveReference>/index.html`*
/// 7. *Redirects the URI path `/<ArchiveName>/data/documentation.json` to the resource on `/<ArchiveName>.doccarchive/data/documentation/<ArchiveReference>.json`*
/// 8. *Redirects the URI path `/<ArchiveName>/favicon.ico` to the resource on `/<ArchiveName>.doccarchive/favicon.ico`*
/// 9. *Redirects the URI path `/<ArchiveName>/favicon.svg` to the resource on `/<ArchiveName>.doccarchive/favicon.svg`*
/// 10. *Redirects the URI path `/<ArchiveName>/theme-settings.json` to the resource on `/<ArchiveName>.doccarchive/theme-settings.json`*
/// 11. *Redirects the URI path `/<ArchiveName>/css/<path/to/file>` to the resource on `/<ArchiveName>.doccarchive/css/<path/to/file>`*
/// 12. *Redirects the URI path `/<ArchiveName>/data/<path/to/file>` to the resource on `/<ArchiveName>.doccarchive/data/<path/to/file>`*
/// 13. *Redirects the URI path `/<ArchiveName>/downloads/<path/to/file>` to the resource on `/<ArchiveName>.doccarchive/downloads/<path/to/file>`*
/// 14. *Redirects the URI path `/<ArchiveName>/images/<path/to/file>` to the resource on `/<ArchiveName>.doccarchive/images/<path/to/file>`*
/// 15. *Redirects the URI path `/<ArchiveName>/img/<path/to/file>` to the resource on `/<ArchiveName>.doccarchive/img/<path/to/file>`*
/// 16. *Redirects the URI path `/<ArchiveName>/index/<path/to/file>` to the resource on `/<ArchiveName>.doccarchive/index/<path/to/file>`*
/// 17. *Redirects the URI path `/<ArchiveName>/js/<path/to/file>` to the resource on `/<ArchiveName>.doccarchive/js/<path/to/file>`*
/// 18. *Redirects the URI path `/<ArchiveName>/videos/<path/to/file>` to the resource on `/<ArchiveName>.doccarchive/videos/<path/to/file>`*
///
public struct DocCMiddleware<FileSystemProvider: FileProvider> { public struct DocCMiddleware<FileSystemProvider: FileProvider> {
// MARK: Properties // MARK: Properties
@@ -102,52 +124,63 @@ extension DocCMiddleware: RouterMiddleware {
// MARK: Functions // MARK: Functions
public func handle( public func handle(
_ input: Input, _ request: Input,
context: any Context, context: any Context,
next: (Input, any Context) async throws -> Output next: (Input, any Context) async throws -> Output
) async throws -> Output { ) async throws -> Output {
guard guard
let uriPath = checkURI(input.uri), let uriPath = checkURI(request.uri),
let uriData = prepareURIPath(uriPath) let uriData = prepareURIPath(uriPath)
else { else {
return try await next(input, context) return try await next(request, context)
} }
if uriData.resourcePath == .Path.forwardSlash { let rootPaths: [String] = [
// rule #1: Redirects URI root to `/`. String(format: .Format.Path.root, uriData.archiveName),
// rule #2: Redirects URI resources with `/` to `/documentation`. String(format: .Format.Path.folder, uriData.archiveName)
]
if rootPaths.contains(uriData.resourcePath) {
return redirectURI( return redirectURI(
uriPath.hasSuffix(.Path.forwardSlash) uriPath.hasSuffix(.Path.forwardSlash)
// Rule #2: Redirects the URI path /<ArchiveName>/ to the path /<ArchiveName>/documentation
? String(format: .Format.Path.documentation, uriPath) ? String(format: .Format.Path.documentation, uriPath)
: String(format: .Format.Path.forwardSlash, uriPath), // Rule #1: Redirects the URI path /<ArchiveName> to the path /<ArchiveName>/
with: (input, context) : String(format: .Format.Path.forwardSlash, uriPath),
with: (request, context)
) )
} }
for assetFile in AssetFile.allCases { for assetFile in AssetFile.allCases {
if uriData.resourcePath.contains(assetFile.path) { if uriData.resourcePath.contains(assetFile.path) {
return try await serveURI( return try await serveURI(
assetFile == .documentation assetFile == .documentation
// Rule #6: Redirects URI resources with `/data/documentation.json` to the file in the `data/documentation/` // Rule #7: Redirects the URI path /<ArchiveName>/data/documentation.json to the resource on /<ArchiveName>.doccarchive/data/documentation/<ArchiveReference>.json
// folder that has the name of the module and ends with the `.json` extension in the *DocC* archive container. ? String(format: .Format.Path.documentationJSON, uriData.archiveReference)
? String(format: .Format.Path.documentationJSON, uriData.archiveName) // Rule #8: Redirects the URI path `/<ArchiveName>/favicon.ico` to the resource on `/<ArchiveName>.doccarchive/favicon.ico`
// Rule #7: Redirect URI resources for static files (`favicon.ico`, `favicon.svg`, and `theme-settings.json`) // Rule #9: Redirects the URI path `/<ArchiveName>/favicon.svg` to the resource on `/<ArchiveName>.doccarchive/favicon.svg`
// to their respective files in the *DocC* archive container. // Rule #10: Redirects the URI path `/<ArchiveName>/theme-settings.json` to the resource on `/<ArchiveName>.doccarchive/theme-settings.json`
: uriData.resourcePath, : uriData.resourcePath,
at: uriData.archivePath, at: uriData.archivePath,
with: (input, context) with: (request, context)
) )
} }
} }
for assetFolder in AssetFolder.allCases { for assetFolder in AssetFolder.allCases {
if uriData.resourcePath.contains(assetFolder.path) { if uriData.resourcePath.contains(assetFolder.path) {
// Rule #8: Redirect URI resources for asset files (`/css/`, `/data/`, `/downloads/`, `/images/`, `/img/`, // Rule #11: Redirects the URI path `/<ArchiveName>/css/<path/to/file>` to the resource on `/<ArchiveName>.doccarchive/css/<path/to/file>`
// `/index/`, `/js/`, or `/videos/`) to their respective files in the *DocC* archive container. // Rule #12: Redirects the URI path `/<ArchiveName>/data/<path/to/file>` to the resource on `/<ArchiveName>.doccarchive/data/<path/to/file>`
// Rule #13: Redirects the URI path `/<ArchiveName>/downloads/<path/to/file>` to the resource on `/<ArchiveName>.doccarchive/downloads/<path/to/file>`
// Rule #14: Redirects the URI path `/<ArchiveName>/images/<path/to/file>` to the resource on `/<ArchiveName>.doccarchive/images/<path/to/file>`
// Rule #15: Redirects the URI path `/<ArchiveName>/img/<path/to/file>` to the resource on `/<ArchiveName>.doccarchive/img/<path/to/file>`
// Rule #16: Redirects the URI path `/<ArchiveName>/index/<path/to/file>` to the resource on `/<ArchiveName>.doccarchive/index/<path/to/file>`
// Rule #17: Redirects the URI path `/<ArchiveName>/js/<path/to/file>` to the resource on `/<ArchiveName>.doccarchive/js/<path/to/file>`
// Rule #18: Redirects the URI path `/<ArchiveName>/videos/<path/to/file>` to the resource on `/<ArchiveName>.doccarchive/videos/<path/to/file>`
return try await serveURI( return try await serveURI(
uriData.resourcePath, uriData.resourcePath,
at: uriData.archivePath, at: uriData.archivePath,
with: (input, context) with: (request, context)
) )
} }
} }
@@ -155,24 +188,25 @@ extension DocCMiddleware: RouterMiddleware {
for documentationFolder in DocumentationFolder.allCases { for documentationFolder in DocumentationFolder.allCases {
if uriData.resourcePath.contains(documentationFolder.path) { if uriData.resourcePath.contains(documentationFolder.path) {
if uriData.resourcePath.hasSuffix(.Path.forwardSlash) { if uriData.resourcePath.hasSuffix(.Path.forwardSlash) {
// Rule #5: Redirect URI resources for `/documentation/` and `/tutorials/` folders to their respective `index.html` file. // Rule #5: Redirects the URI path /<ArchiveName>/documentation/ to the resource on /<ArchiveName>.doccarchive/documentation/<ArchiveReference>/index.html
// Rule #6: Redirects the URI path /<ArchiveName>/tutorials/ to the resource on /<ArchiveName>.doccarchive/tutorials/<ArchiveReference>/index.html
return try await serveURI( return try await serveURI(
String(format: .Format.Path.index, documentationFolder.path, uriData.archiveName), String(format: .Format.Path.index, documentationFolder.path, uriData.archiveReference),
at: uriData.archivePath, at: uriData.archivePath,
with: (input, context) with: (request, context)
) )
} else { } else {
// rule #3: Redirects URI resources with `/documentation` to `/documentation/`. // Rule #3: Redirects the URI path /<ArchiveName>/documentation to the path /<ArchiveName>/documentation/
// rule #4: Redirects URI resources with `/tutorials` to `/tutorials/`. // Rule #4: Redirects the URI path /<ArchiveName>/tutorials to the path /<ArchiveName>/tutorials/
return redirectURI( return redirectURI(
String(format: .Format.Path.forwardSlash, uriPath), String(format: .Format.Path.forwardSlash, uriPath),
with: (input, context) with: (request, context)
) )
} }
} }
} }
return try await next(input, context) return try await next(request, context)
} }
} }