Initial commit.
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
import Configuration
|
||||
|
||||
extension AbsoluteConfigKey {
|
||||
/// A namespace for the analytics configuration keys, as absolute keys.
|
||||
public enum Analytics {
|
||||
/// The absolute configuration key for the analytics website identifier.
|
||||
public static let websiteID: AbsoluteConfigKey = .init(.Analytics.websiteID)
|
||||
/// The absolute configuration key for the comma-delimited domains the tracker reports from.
|
||||
public static let domains: AbsoluteConfigKey = .init(.Analytics.domains)
|
||||
/// The absolute configuration key for recorder mode, loading the session recorder script alongside the tracker.
|
||||
public static let recorder: AbsoluteConfigKey = .init(.Analytics.recorder)
|
||||
}
|
||||
/// A namespace for the static files cache configuration keys, as absolute keys.
|
||||
public enum Cache {
|
||||
/// The absolute configuration key for the max-age, in seconds, applied to fingerprinted assets and fonts.
|
||||
public static let maxAgeAsset: AbsoluteConfigKey = .init(.Cache.maxAgeAsset)
|
||||
/// The absolute configuration key for the max-age, in seconds, applied to unversioned text-based static files.
|
||||
public static let maxAgeText: AbsoluteConfigKey = .init(.Cache.maxAgeText)
|
||||
/// The absolute configuration key for the max-age, in seconds, applied to image static files.
|
||||
public static let maxAgeImage: AbsoluteConfigKey = .init(.Cache.maxAgeImage)
|
||||
/// The absolute configuration key for the max-age, in seconds, applied to all other static files.
|
||||
public static let maxAgeDefault: AbsoluteConfigKey = .init(.Cache.maxAgeDefault)
|
||||
}
|
||||
/// A namespace for the response compression configuration keys, as absolute keys.
|
||||
public enum Compression {
|
||||
/// The absolute configuration key for the minimum response body size, in bytes, before compression is applied.
|
||||
public static let minResponseSize: AbsoluteConfigKey = .init(.Compression.minResponseSize)
|
||||
}
|
||||
/// A namespace for the persistence configuration keys, as absolute keys.
|
||||
public enum Database {
|
||||
/// The absolute configuration key selecting migrate-and-exit mode.
|
||||
public static let migrate: AbsoluteConfigKey = .init(.Database.migrate)
|
||||
/// The absolute configuration key for the persistence driver.
|
||||
public static let driver: AbsoluteConfigKey = .init(.Database.driver)
|
||||
/// The absolute configuration key for the PostgreSQL host.
|
||||
public static let host: AbsoluteConfigKey = .init(.Database.host)
|
||||
/// The absolute configuration key for the PostgreSQL port.
|
||||
public static let port: AbsoluteConfigKey = .init(.Database.port)
|
||||
/// The absolute configuration key for the database name.
|
||||
public static let name: AbsoluteConfigKey = .init(.Database.name)
|
||||
/// The absolute configuration key for the database username.
|
||||
public static let username: AbsoluteConfigKey = .init(.Database.username)
|
||||
/// The absolute configuration key for the database password.
|
||||
public static let password: AbsoluteConfigKey = .init(.Database.password)
|
||||
/// The absolute configuration key for the TLS posture used when connecting.
|
||||
public static let tls: AbsoluteConfigKey = .init(.Database.tls)
|
||||
/// The absolute configuration key for the maximum pooled connections per event loop.
|
||||
public static let poolMaxPerEventLoop: AbsoluteConfigKey = .init(.Database.poolMaxPerEventLoop)
|
||||
/// The absolute configuration key for the longest wait, in seconds, for a pooled connection to become available.
|
||||
public static let poolTimeout: AbsoluteConfigKey = .init(.Database.poolTimeout)
|
||||
}
|
||||
/// A namespace for the HTTP server configuration keys, as absolute keys.
|
||||
public enum HTTP {
|
||||
/// The absolute configuration key for the host the server binds to.
|
||||
public static let host: AbsoluteConfigKey = .init(.HTTP.host)
|
||||
/// The absolute configuration key for the port the server listens on.
|
||||
public static let port: AbsoluteConfigKey = .init(.HTTP.port)
|
||||
/// The absolute configuration key for the server's name.
|
||||
public static let serverName: AbsoluteConfigKey = .init(.HTTP.serverName)
|
||||
}
|
||||
/// A namespace for the logging configuration keys, as absolute keys.
|
||||
public enum Log {
|
||||
/// The absolute configuration key for the minimum log level.
|
||||
public static let level: AbsoluteConfigKey = .init(.Log.level)
|
||||
}
|
||||
/// A namespace for the rate limit configuration keys, as absolute keys.
|
||||
public enum RateLimit {
|
||||
/// The absolute configuration key for the number of requests admitted per client per window.
|
||||
public static let limit: AbsoluteConfigKey = .init(.RateLimit.limit)
|
||||
/// The absolute configuration key for the window length, in seconds.
|
||||
public static let window: AbsoluteConfigKey = .init(.RateLimit.window)
|
||||
/// The absolute configuration key for keying clients by the first `X-Forwarded-For` entry.
|
||||
public static let trustForwardedFor: AbsoluteConfigKey = .init(.RateLimit.trustForwardedFor)
|
||||
}
|
||||
/// A namespace for the path configuration keys, as absolute keys.
|
||||
public enum Path {
|
||||
/// The absolute configuration key for the directory the static files are served from.
|
||||
public static let staticFiles: AbsoluteConfigKey = .init(.Path.staticFiles)
|
||||
}
|
||||
/// A namespace for the security headers configuration keys, as absolute keys.
|
||||
public enum Security {
|
||||
/// The absolute configuration key for the `Content-Security-Policy` header value.
|
||||
public static let contentSecurityPolicy: AbsoluteConfigKey = .init(.Security.contentSecurityPolicy)
|
||||
/// The absolute configuration key for the `X-Content-Type-Options` header value.
|
||||
public static let contentTypeOptions: AbsoluteConfigKey = .init(.Security.contentTypeOptions)
|
||||
/// The absolute configuration key for the `X-Frame-Options` header value.
|
||||
public static let frameOptions: AbsoluteConfigKey = .init(.Security.frameOptions)
|
||||
/// The absolute configuration key for the `Referrer-Policy` header value.
|
||||
public static let referrerPolicy: AbsoluteConfigKey = .init(.Security.referrerPolicy)
|
||||
/// The absolute configuration key for the `Permissions-Policy` header value.
|
||||
public static let permissionsPolicy: AbsoluteConfigKey = .init(.Security.permissionsPolicy)
|
||||
/// The absolute configuration key for the `Strict-Transport-Security` header value.
|
||||
public static let strictTransportSecurity: AbsoluteConfigKey = .init(.Security.strictTransportSecurity)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
import Configuration
|
||||
|
||||
extension ConfigKey {
|
||||
/// A namespace for the analytics configuration keys.
|
||||
public enum Analytics {
|
||||
/// The configuration key for the analytics website identifier (cleared to disable analytics).
|
||||
public static let websiteID: ConfigKey = "analytics.websiteID"
|
||||
/// The configuration key for the comma-delimited domains the tracker reports from.
|
||||
public static let domains: ConfigKey = "analytics.domains"
|
||||
/// The configuration key for recorder mode, loading the session recorder script alongside the tracker (set to `false` to disable).
|
||||
public static let recorder: ConfigKey = "analytics.recorder"
|
||||
}
|
||||
/// A namespace for the static files cache configuration keys.
|
||||
public enum Cache {
|
||||
/// The configuration key for the max-age, in seconds, applied to fingerprinted assets (CSS, JavaScript) and fonts.
|
||||
public static let maxAgeAsset: ConfigKey = "cache.maxAge.asset"
|
||||
/// The configuration key for the max-age, in seconds, applied to unversioned text-based static files (e.g. plain text).
|
||||
public static let maxAgeText: ConfigKey = "cache.maxAge.text"
|
||||
/// The configuration key for the max-age, in seconds, applied to image static files (ICO, PNG, SVG).
|
||||
public static let maxAgeImage: ConfigKey = "cache.maxAge.image"
|
||||
/// The configuration key for the max-age, in seconds, applied to all other static files (e.g. the web manifest).
|
||||
public static let maxAgeDefault: ConfigKey = "cache.maxAge.default"
|
||||
}
|
||||
/// A namespace for the response compression configuration keys.
|
||||
public enum Compression {
|
||||
/// The configuration key for the minimum response body size, in bytes, before compression is applied.
|
||||
public static let minResponseSize: ConfigKey = "compression.minimumResponseSize"
|
||||
}
|
||||
/// A namespace for the persistence configuration keys.
|
||||
public enum Database {
|
||||
/// The configuration key selecting migrate-and-exit mode (run migrations, then exit) instead of serving.
|
||||
public static let migrate: ConfigKey = "database.migrate"
|
||||
/// The configuration key for the persistence driver (`inMemory` or `postgres`).
|
||||
public static let driver: ConfigKey = "database.driver"
|
||||
/// The configuration key for the PostgreSQL host.
|
||||
public static let host: ConfigKey = "database.host"
|
||||
/// The configuration key for the PostgreSQL port.
|
||||
public static let port: ConfigKey = "database.port"
|
||||
/// The configuration key for the database name.
|
||||
public static let name: ConfigKey = "database.name"
|
||||
/// The configuration key for the database username.
|
||||
public static let username: ConfigKey = "database.username"
|
||||
/// The configuration key for the database password.
|
||||
public static let password: ConfigKey = "database.password"
|
||||
/// The configuration key for the TLS posture used when connecting (`off`, `prefer`, or `require`).
|
||||
public static let tls: ConfigKey = "database.tls"
|
||||
/// The configuration key for the maximum pooled connections per event loop.
|
||||
public static let poolMaxPerEventLoop: ConfigKey = "database.pool.maxPerEventLoop"
|
||||
/// The configuration key for the longest wait, in seconds, for a pooled connection to become available.
|
||||
public static let poolTimeout: ConfigKey = "database.pool.timeout"
|
||||
}
|
||||
/// A namespace for the HTTP server configuration keys.
|
||||
public enum HTTP {
|
||||
/// The configuration key for the host the server binds to.
|
||||
public static let host: ConfigKey = "http.host"
|
||||
/// The configuration key for the port the server listens on.
|
||||
public static let port: ConfigKey = "http.port"
|
||||
/// The configuration key for the server's name.
|
||||
public static let serverName: ConfigKey = "http.serverName"
|
||||
}
|
||||
/// A namespace for the logging configuration keys.
|
||||
public enum Log {
|
||||
/// The configuration key for the minimum log level.
|
||||
public static let level: ConfigKey = "log.level"
|
||||
}
|
||||
/// A namespace for the rate limit configuration keys.
|
||||
public enum RateLimit {
|
||||
/// The configuration key for the number of requests admitted per client per window.
|
||||
public static let limit: ConfigKey = "rateLimit.limit"
|
||||
/// The configuration key for the window length, in seconds.
|
||||
public static let window: ConfigKey = "rateLimit.window"
|
||||
/// The configuration key for keying clients by the first `X-Forwarded-For` entry (enable only behind a trusted proxy).
|
||||
public static let trustForwardedFor: ConfigKey = "rateLimit.trustForwardedFor"
|
||||
}
|
||||
/// A namespace for the path configuration keys.
|
||||
public enum Path {
|
||||
/// The configuration key for the directory the static files are served from.
|
||||
public static let staticFiles: ConfigKey = "path.staticFiles"
|
||||
}
|
||||
/// A namespace for the security headers configuration keys.
|
||||
public enum Security {
|
||||
/// The configuration key for the `Content-Security-Policy` header value.
|
||||
public static let contentSecurityPolicy: ConfigKey = "security.contentSecurityPolicy"
|
||||
/// The configuration key for the `X-Content-Type-Options` header value.
|
||||
public static let contentTypeOptions: ConfigKey = "security.contentTypeOptions"
|
||||
/// The configuration key for the `X-Frame-Options` header value.
|
||||
public static let frameOptions: ConfigKey = "security.frameOptions"
|
||||
/// The configuration key for the `Referrer-Policy` header value.
|
||||
public static let referrerPolicy: ConfigKey = "security.referrerPolicy"
|
||||
/// The configuration key for the `Permissions-Policy` header value.
|
||||
public static let permissionsPolicy: ConfigKey = "security.permissionsPolicy"
|
||||
/// The configuration key for the `Strict-Transport-Security` header value (omitted when unset).
|
||||
public static let strictTransportSecurity: ConfigKey = "security.strictTransportSecurity"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import Configuration
|
||||
|
||||
extension ConfigValue {
|
||||
/// A namespace for the HTTP server's default configuration values.
|
||||
public enum HTTP {
|
||||
/// The default server name.
|
||||
public static let serverName: ConfigValue = .init(stringLiteral: .Server.name)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
extension Int {
|
||||
/// A namespace for the cache's default configuration values.
|
||||
public enum Cache {
|
||||
/// The default max-age, in seconds, applied to fingerprinted assets and fonts (1 year).
|
||||
public static let maxAgeAsset = 31_536_000
|
||||
/// The default max-age, in seconds, applied to unversioned text-based static files (1 hour).
|
||||
public static let maxAgeText = 3_600
|
||||
/// The default max-age, in seconds, applied to image static files (1 week).
|
||||
public static let maxAgeImage = 604_800
|
||||
/// The default max-age, in seconds, applied to all other static files (1 day).
|
||||
public static let maxAgeDefault = 86_400
|
||||
}
|
||||
/// A namespace for the response compression's default configuration values.
|
||||
public enum Compression {
|
||||
/// The default minimum response body size, in bytes, before compression is applied (1 KB).
|
||||
public static let minResponseSize = 1_024
|
||||
}
|
||||
/// A namespace for the persistence's default configuration values.
|
||||
public enum Database {
|
||||
/// The default PostgreSQL port.
|
||||
public static let port = 5_432
|
||||
/// The default maximum pooled connections per event loop.
|
||||
public static let poolMaxPerEventLoop = 4
|
||||
/// The default longest wait, in seconds, for a pooled connection to become available (the driver's own default).
|
||||
public static let poolTimeout = 10
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import Foundation
|
||||
import Localization
|
||||
|
||||
public extension LanguageList {
|
||||
|
||||
// MARK: Initializers
|
||||
|
||||
/// Creates a language list backed by the module's String Catalog.
|
||||
init() {
|
||||
self.init(bundle: .module)
|
||||
}
|
||||
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
import Foundation
|
||||
import Infrastructure
|
||||
|
||||
public extension LocalizationMiddleware {
|
||||
|
||||
// MARK: Initializers
|
||||
|
||||
/// Creates a localization middleware that negotiates against the module's String Catalog languages.
|
||||
init() {
|
||||
self.init(bundle: .module)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import Foundation
|
||||
import Infrastructure
|
||||
|
||||
public extension NotFoundMiddleware {
|
||||
|
||||
// MARK: Initializers
|
||||
|
||||
/// Creates a not-found middleware that renders the website's error page, localized to the module's String Catalog languages.
|
||||
/// - Parameters:
|
||||
/// - assetVersion: the version token appended to the page's asset URLs, or `nil` (the default) to leave them unversioned.
|
||||
/// - analytics: the analytics tracker the error page embeds, or `nil` (the default) to omit it.
|
||||
init(
|
||||
assetVersion: String? = nil,
|
||||
analytics: Analytics? = nil
|
||||
) {
|
||||
self.init(bundle: .module) {
|
||||
NotFoundPage(
|
||||
locale: $0,
|
||||
assetVersion: assetVersion,
|
||||
analytics: analytics
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
extension String {
|
||||
/// A namespace for the analytics default configuration values.
|
||||
///
|
||||
/// Analytics ships **off**: ``websiteID`` is empty, so the pages embed no tracker until a deployment sets `analytics.websiteID`. Point
|
||||
/// ``origin`` at your own instance before enabling it — the placeholder is an [RFC 2606](https://www.rfc-editor.org/rfc/rfc2606)
|
||||
/// reserved domain, so an unconfigured copy can never report to someone else's server.
|
||||
public enum Analytics {
|
||||
/// The origin the analytics scripts are loaded from and their beacons are sent to (scheme and host, no trailing slash).
|
||||
///
|
||||
/// Single-sourced here: both ``scriptURL`` and the session recorder script the pages embed in recorder mode derive from this
|
||||
/// constant. It is deliberately not a configuration key — the `Content-Security-Policy` must allow the same origin, and a value that
|
||||
/// can drift at runtime would silently break the tracker it is supposed to permit.
|
||||
public static let origin = "https://analytics.example.com"
|
||||
/// The URL the analytics tracker script is loaded from.
|
||||
public static let scriptURL = "\(origin)/script"
|
||||
/// The default analytics website identifier the tracker reports as: empty, which omits the tracker entirely.
|
||||
public static let websiteID = ""
|
||||
/// The default comma-delimited domains the tracker reports from: empty, which reports from every host.
|
||||
///
|
||||
/// Once set, keep it paired with the host the pages are served at — a deployment that serves from another host without matching
|
||||
/// `analytics.domains` reports from a host it no longer serves, so analytics silently records nothing.
|
||||
public static let domains = ""
|
||||
}
|
||||
/// A namespace for the persistence's default configuration values and recognized tokens.
|
||||
public enum Database {
|
||||
/// The default persistence driver: in-memory SQLite, which needs no external infrastructure.
|
||||
public static let driver = "inMemory"
|
||||
/// The driver token selecting the PostgreSQL backend.
|
||||
public static let driverPostgres = "postgres"
|
||||
/// The default PostgreSQL host.
|
||||
public static let host = "localhost"
|
||||
/// The default database name.
|
||||
public static let name = "ccn"
|
||||
/// The default database username.
|
||||
public static let username = "ccn"
|
||||
/// The default TLS posture token.
|
||||
public static let tls = "prefer"
|
||||
/// The TLS token disabling TLS.
|
||||
public static let tlsOff = "off"
|
||||
/// The TLS token requiring TLS.
|
||||
public static let tlsRequire = "require"
|
||||
}
|
||||
/// A namespace for well-known path string constants.
|
||||
public enum Path {
|
||||
/// The directory, relative to the working directory, that the website's static files are served from.
|
||||
public static let staticResources = "Resources/Static"
|
||||
}
|
||||
/// A namespace for the server string constants.
|
||||
public enum Server {
|
||||
/// The website server's name.
|
||||
public static let name = "CCNWebsite"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user