Implemented phase 2 for the POC prototype in the Website service.
This commit is contained in:
1 parent
09c7b8a1a7
commit
43dcc03da6
28 files changed
+1468
-65
No files matched your search
@@ -0,0 +1,8 @@
|
||||
import HTTPTypes
|
||||
|
||||
extension HTTPFields {
|
||||
/// The headers of a page rendered for its viewer: kept out of shared caches, and revalidated before reuse.
|
||||
static let perViewer: Self = [
|
||||
.cacheControl: "private, no-cache"
|
||||
]
|
||||
}
|
||||
@@ -33,11 +33,34 @@ extension String {
|
||||
static let series = "/series"
|
||||
/// The passkey sign-in screen.
|
||||
static let signIn = "/sign-in"
|
||||
/// The path a sign-out is posted to.
|
||||
static let signOut = "/sign-out"
|
||||
/// The terms of service.
|
||||
static let terms = "/terms"
|
||||
/// The want list.
|
||||
static let wants = "/wants"
|
||||
|
||||
/// The path buying a listing outright starts at.
|
||||
/// - Parameter slug: the listing's URL slug.
|
||||
static func buy(
|
||||
_ slug: String
|
||||
) -> String {
|
||||
"\(listing(slug))/buy"
|
||||
}
|
||||
|
||||
/// The path a gated action leads to: the action itself when signed in, else the sign-in screen, returning to where the action was taken.
|
||||
/// - Parameters:
|
||||
/// - path: the action's own path.
|
||||
/// - origin: the path of the screen the action is taken on.
|
||||
/// - viewer: the signed-in viewer, or `nil` when signed out.
|
||||
static func gated(
|
||||
_ path: String,
|
||||
from origin: String,
|
||||
viewer: Viewer?
|
||||
) -> String {
|
||||
viewer == nil ? signIn(returningTo: origin) : path
|
||||
}
|
||||
|
||||
/// The path of a listing's detail screen.
|
||||
/// - Parameter slug: the listing's URL slug.
|
||||
static func listing(
|
||||
@@ -46,6 +69,30 @@ extension String {
|
||||
"/listing/\(slug)"
|
||||
}
|
||||
|
||||
/// The path of a new message thread about a listing.
|
||||
/// - Parameter slug: the listing's URL slug.
|
||||
static func message(
|
||||
listing slug: String
|
||||
) -> String {
|
||||
"\(messages)/new?listing=\(slug)"
|
||||
}
|
||||
|
||||
/// The path of a new message thread with a shop.
|
||||
/// - Parameter slug: the shop's URL slug.
|
||||
static func message(
|
||||
shop slug: String
|
||||
) -> String {
|
||||
"\(messages)/new?shop=\(slug)"
|
||||
}
|
||||
|
||||
/// The path of the offer sheet for a listing.
|
||||
/// - Parameter slug: the listing's URL slug.
|
||||
static func offer(
|
||||
_ slug: String
|
||||
) -> String {
|
||||
"\(listing(slug))/offer"
|
||||
}
|
||||
|
||||
/// The path of a shop's public screen.
|
||||
/// - Parameter slug: the shop's URL slug.
|
||||
static func shop(
|
||||
@@ -54,6 +101,27 @@ extension String {
|
||||
"/shop/\(slug)"
|
||||
}
|
||||
|
||||
/// The path to return to after signing in: the one asked for when it stays on this site, else the home screen.
|
||||
///
|
||||
/// Only a rooted path is followed. Anything else — another origin, a scheme-relative `//host`, a backslash a browser reads as a slash —
|
||||
/// would turn the sign-in into an open redirect.
|
||||
/// - Parameter path: the path asked for, as the `return` parameter carried it.
|
||||
static func safeReturn(
|
||||
_ path: String?
|
||||
) -> String {
|
||||
guard
|
||||
let path,
|
||||
path.hasPrefix("/"),
|
||||
!path.hasPrefix("//"),
|
||||
!path.contains("\\"),
|
||||
!path.unicodeScalars.contains(where: { $0.properties.generalCategory == .control })
|
||||
else {
|
||||
return home
|
||||
}
|
||||
|
||||
return path
|
||||
}
|
||||
|
||||
/// The path of the sign-in screen, returning to the given path afterwards.
|
||||
/// - Parameter path: the path the visitor's gated action started from.
|
||||
static func signIn(
|
||||
|
||||
Reference in new issue
Block a user