diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..7cfbe01
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,7 @@
+root = true
+
+[*.swift]
+indent_style = space
+indent_size = 2
+tab_width = 2
+trim_trailing_whitespace = true
diff --git a/.nvim/xcodebuild/devices.json b/.nvim/xcodebuild/devices.json
new file mode 100644
index 0000000..255ae8c
--- /dev/null
+++ b/.nvim/xcodebuild/devices.json
@@ -0,0 +1 @@
+{"projectFile": "/Volumes/Bucket/Repos/hello/Package.swift", "scheme": "hello", "devices": [{"id": "00008103-000E79D011EA001E", "name": "My Mac", "arch": "arm64e", "platform": "macOS"}]}
diff --git a/.nvim/xcodebuild/settings.json b/.nvim/xcodebuild/settings.json
new file mode 100644
index 0000000..7308876
--- /dev/null
+++ b/.nvim/xcodebuild/settings.json
@@ -0,0 +1 @@
+{"deviceName": "My Mac", "workingDirectory": "/Volumes/Bucket/Repos/hello", "swiftPackage": "/Volumes/Bucket/Repos/hello/Package.swift", "platform": "macOS", "destination": "00008103-000E79D011EA001E", "scheme": "hello"}
diff --git a/.swiftformat b/.swiftformat
new file mode 100644
index 0000000..08f338e
--- /dev/null
+++ b/.swiftformat
@@ -0,0 +1,11 @@
+--self init-only
+--indent 2
+--ifdef indent
+--trimwhitespace always
+--wraparguments before-first
+--wrapparameters before-first
+--wrapcollections preserve
+--wrapconditions after-first
+--typeblanklines preserve
+--commas inline
+--stripunusedargs closure-only
diff --git a/.swiftlint.yml b/.swiftlint.yml
new file mode 100644
index 0000000..213129c
--- /dev/null
+++ b/.swiftlint.yml
@@ -0,0 +1,11 @@
+disabled_rules:
+ - closing_brace
+ - fuction_body_length
+ - opening_brace
+ - nesting
+
+included:
+ - Sources
+ - Tests
+
+ignore_multiline_statement_conditions: true
diff --git a/Package.resolved b/Package.resolved
new file mode 100644
index 0000000..33b2e7a
--- /dev/null
+++ b/Package.resolved
@@ -0,0 +1,294 @@
+{
+ "originHash" : "f7e5f899f6a75ee8db21a1bb07e44cbbdf4d7aaafddefc76f56bd4f3c446abd7",
+ "pins" : [
+ {
+ "identity" : "async-http-client",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/swift-server/async-http-client.git",
+ "state" : {
+ "revision" : "2119f0d9cc1b334e25447fe43d3693c0e60e6234",
+ "version" : "1.24.0"
+ }
+ },
+ {
+ "identity" : "async-kit",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/vapor/async-kit.git",
+ "state" : {
+ "revision" : "e048c8ee94967e8d8a1c2ec0e1156d6f7fa34d31",
+ "version" : "1.20.0"
+ }
+ },
+ {
+ "identity" : "console-kit",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/vapor/console-kit.git",
+ "state" : {
+ "revision" : "966d89ae64cd71c652a1e981bc971de59d64f13d",
+ "version" : "4.15.1"
+ }
+ },
+ {
+ "identity" : "fluent",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/vapor/fluent.git",
+ "state" : {
+ "revision" : "223b27d04ab2b51c25503c9922eecbcdf6c12f89",
+ "version" : "4.12.0"
+ }
+ },
+ {
+ "identity" : "fluent-kit",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/vapor/fluent-kit.git",
+ "state" : {
+ "revision" : "614d3ec27cdef50cfb9fc3cfd382b6a4d9578cff",
+ "version" : "1.49.0"
+ }
+ },
+ {
+ "identity" : "fluent-sqlite-driver",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/vapor/fluent-sqlite-driver.git",
+ "state" : {
+ "revision" : "6e3a5ff7f2cb733771a6bd71dd3a491cce79f24d",
+ "version" : "4.8.0"
+ }
+ },
+ {
+ "identity" : "leaf",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/vapor/leaf.git",
+ "state" : {
+ "revision" : "bf48d2423c00292b5937c60166c7db99705cae47",
+ "version" : "4.4.1"
+ }
+ },
+ {
+ "identity" : "leaf-kit",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/vapor/leaf-kit.git",
+ "state" : {
+ "revision" : "d0ca4417166ef7868d28ad21bc77d36b8735a0fc",
+ "version" : "1.11.1"
+ }
+ },
+ {
+ "identity" : "multipart-kit",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/vapor/multipart-kit.git",
+ "state" : {
+ "revision" : "3498e60218e6003894ff95192d756e238c01f44e",
+ "version" : "4.7.1"
+ }
+ },
+ {
+ "identity" : "routing-kit",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/vapor/routing-kit.git",
+ "state" : {
+ "revision" : "8c9a227476555c55837e569be71944e02a056b72",
+ "version" : "4.9.1"
+ }
+ },
+ {
+ "identity" : "sql-kit",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/vapor/sql-kit.git",
+ "state" : {
+ "revision" : "e0b35ff07601465dd9f3af19a1c23083acaae3bd",
+ "version" : "3.32.0"
+ }
+ },
+ {
+ "identity" : "sqlite-kit",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/vapor/sqlite-kit.git",
+ "state" : {
+ "revision" : "f35a863ecc2da5d563b836a9a696b148b0f4169f",
+ "version" : "4.5.2"
+ }
+ },
+ {
+ "identity" : "sqlite-nio",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/vapor/sqlite-nio.git",
+ "state" : {
+ "revision" : "0c6a711c9779b5493364631e4f014618ef12a40a",
+ "version" : "1.10.5"
+ }
+ },
+ {
+ "identity" : "swift-algorithms",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-algorithms.git",
+ "state" : {
+ "revision" : "f6919dfc309e7f1b56224378b11e28bab5bccc42",
+ "version" : "1.2.0"
+ }
+ },
+ {
+ "identity" : "swift-asn1",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-asn1.git",
+ "state" : {
+ "revision" : "7faebca1ea4f9aaf0cda1cef7c43aecd2311ddf6",
+ "version" : "1.3.0"
+ }
+ },
+ {
+ "identity" : "swift-atomics",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-atomics.git",
+ "state" : {
+ "revision" : "cd142fd2f64be2100422d658e7411e39489da985",
+ "version" : "1.2.0"
+ }
+ },
+ {
+ "identity" : "swift-collections",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-collections.git",
+ "state" : {
+ "revision" : "671108c96644956dddcd89dd59c203dcdb36cec7",
+ "version" : "1.1.4"
+ }
+ },
+ {
+ "identity" : "swift-crypto",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-crypto.git",
+ "state" : {
+ "revision" : "ff0f781cf7c6a22d52957e50b104f5768b50c779",
+ "version" : "3.10.0"
+ }
+ },
+ {
+ "identity" : "swift-distributed-tracing",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-distributed-tracing.git",
+ "state" : {
+ "revision" : "6483d340853a944c96dbcc28b27dd10b6c581703",
+ "version" : "1.1.2"
+ }
+ },
+ {
+ "identity" : "swift-http-types",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-http-types",
+ "state" : {
+ "revision" : "ef18d829e8b92d731ad27bb81583edd2094d1ce3",
+ "version" : "1.3.1"
+ }
+ },
+ {
+ "identity" : "swift-log",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-log.git",
+ "state" : {
+ "revision" : "96a2f8a0fa41e9e09af4585e2724c4e825410b91",
+ "version" : "1.6.2"
+ }
+ },
+ {
+ "identity" : "swift-metrics",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-metrics.git",
+ "state" : {
+ "revision" : "e0165b53d49b413dd987526b641e05e246782685",
+ "version" : "2.5.0"
+ }
+ },
+ {
+ "identity" : "swift-nio",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-nio.git",
+ "state" : {
+ "revision" : "dca6594f65308c761a9c409e09fbf35f48d50d34",
+ "version" : "2.77.0"
+ }
+ },
+ {
+ "identity" : "swift-nio-extras",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-nio-extras.git",
+ "state" : {
+ "revision" : "2e9746cfc57554f70b650b021b6ae4738abef3e6",
+ "version" : "1.24.1"
+ }
+ },
+ {
+ "identity" : "swift-nio-http2",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-nio-http2.git",
+ "state" : {
+ "revision" : "170f4ca06b6a9c57b811293cebcb96e81b661310",
+ "version" : "1.35.0"
+ }
+ },
+ {
+ "identity" : "swift-nio-ssl",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-nio-ssl.git",
+ "state" : {
+ "revision" : "c7e95421334b1068490b5d41314a50e70bab23d1",
+ "version" : "2.29.0"
+ }
+ },
+ {
+ "identity" : "swift-nio-transport-services",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-nio-transport-services.git",
+ "state" : {
+ "revision" : "bbd5e63cf949b7db0c9edaf7a21e141c52afe214",
+ "version" : "1.23.0"
+ }
+ },
+ {
+ "identity" : "swift-numerics",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-numerics.git",
+ "state" : {
+ "revision" : "0a5bc04095a675662cf24757cc0640aa2204253b",
+ "version" : "1.0.2"
+ }
+ },
+ {
+ "identity" : "swift-service-context",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-service-context.git",
+ "state" : {
+ "revision" : "0c62c5b4601d6c125050b5c3a97f20cce881d32b",
+ "version" : "1.1.0"
+ }
+ },
+ {
+ "identity" : "swift-system",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-system.git",
+ "state" : {
+ "revision" : "c8a44d836fe7913603e246acab7c528c2e780168",
+ "version" : "1.4.0"
+ }
+ },
+ {
+ "identity" : "vapor",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/vapor/vapor.git",
+ "state" : {
+ "revision" : "4d7456c0d4b33ef82783a90ecfeae33a52a3972a",
+ "version" : "4.111.0"
+ }
+ },
+ {
+ "identity" : "websocket-kit",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/vapor/websocket-kit.git",
+ "state" : {
+ "revision" : "4232d34efa49f633ba61afde365d3896fc7f8740",
+ "version" : "2.15.0"
+ }
+ }
+ ],
+ "version" : 3
+}
diff --git a/Package.swift b/Package.swift
index f594c27..3d35799 100644
--- a/Package.swift
+++ b/Package.swift
@@ -2,39 +2,45 @@
import PackageDescription
let package = Package(
- name: "hello",
- platforms: [
- .macOS(.v13)
- ],
- dependencies: [
- // 💧 A server-side Swift web framework.
- .package(url: "https://github.com/vapor/vapor.git", from: "4.110.1"),
- // 🔵 Non-blocking, event-driven networking for Swift. Used for custom executors
- .package(url: "https://github.com/apple/swift-nio.git", from: "2.65.0"),
- ],
- targets: [
- .executableTarget(
- name: "App",
- dependencies: [
- .product(name: "Vapor", package: "vapor"),
- .product(name: "NIOCore", package: "swift-nio"),
- .product(name: "NIOPosix", package: "swift-nio"),
- ],
- swiftSettings: swiftSettings
- ),
- .testTarget(
- name: "AppTests",
- dependencies: [
- .target(name: "App"),
- .product(name: "VaporTesting", package: "vapor"),
- ],
- swiftSettings: swiftSettings
- )
- ],
- swiftLanguageModes: [.v5]
+ name: "hello",
+ platforms: [
+ .macOS(.v13)
+ ],
+ dependencies: [
+ // 💧 A server-side Swift web framework.
+ .package(url: "https://github.com/vapor/vapor.git", from: "4.110.1"),
+ // 🔵 Non-blocking, event-driven networking for Swift. Used for custom executors
+ .package(url: "https://github.com/apple/swift-nio.git", from: "2.65.0"),
+ .package(url: "https://github.com/vapor/fluent.git", from: "4.0.0"),
+ .package(url: "https://github.com/vapor/fluent-sqlite-driver.git", from: "4.0.0"),
+ .package(url: "https://github.com/vapor/leaf.git", from: "4.4.0")
+ ],
+ targets: [
+ .executableTarget(
+ name: "App",
+ dependencies: [
+ .product(name: "Vapor", package: "vapor"),
+ .product(name: "NIOCore", package: "swift-nio"),
+ .product(name: "NIOPosix", package: "swift-nio"),
+ .product(name: "FluentSQLiteDriver", package: "fluent-sqlite-driver"),
+ .product(name: "Fluent", package: "fluent"),
+ .product(name: "Leaf", package: "leaf")
+ ],
+ swiftSettings: swiftSettings
+ ),
+ .testTarget(
+ name: "AppTests",
+ dependencies: [
+ .target(name: "App"),
+ .product(name: "VaporTesting", package: "vapor")
+ ],
+ swiftSettings: swiftSettings
+ )
+ ],
+ swiftLanguageModes: [.v5]
)
var swiftSettings: [SwiftSetting] { [
- .enableUpcomingFeature("DisableOutwardActorInference"),
- .enableExperimentalFeature("StrictConcurrency"),
+ .enableUpcomingFeature("DisableOutwardActorInference"),
+ .enableExperimentalFeature("StrictConcurrency")
] }
diff --git a/Public/js/site.js b/Public/js/site.js
new file mode 100644
index 0000000..ac02797
--- /dev/null
+++ b/Public/js/site.js
@@ -0,0 +1,35 @@
+function buildJsonFormData(form) {
+ const jsonFormData = {};
+ for (const pair of new FormData(form)) {
+ jsonFormData[pair[0]] = pair[1];
+ }
+ return jsonFormData
+}
+
+async function postLoginForm(json) {
+ try {
+ const response = await fetch("api/user", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json"
+ },
+ body: JSON.stringify(json)
+ });
+ if (!response.ok) {
+ throw new Error(`Response status: ${response.status}`);
+ }
+ const jsonData = await response.json();
+ console.log(jsonData);
+ } catch (error) {
+ console.error(error.message);
+ }
+}
+
+async function submitLoginForm() {
+ const loginForm = document.querySelector("#loginForm");
+ if (loginForm) {
+ const jsonData = buildJsonFormData(loginForm);
+ console.log(JSON.stringify(jsonData));
+ await postLoginForm(jsonData);
+ }
+}
diff --git a/Public/styles/site.css b/Public/styles/site.css
new file mode 100644
index 0000000..49cbd81
--- /dev/null
+++ b/Public/styles/site.css
@@ -0,0 +1,95 @@
+body {
+ background-color: #1E1E2E;
+ color: white;
+}
+
+p {
+ font-family: verdana;
+ font-size: 20px;
+}
+
+h1 {
+ color: violet;
+}
+
+h2 {
+ color: violet;
+}
+
+input[type=text] {
+ border 2px solid red;
+ border-radius 4px;
+ margin: 8px 0;
+ padding: 12px 20px;
+ width: 50%;
+ box-sizing: border-box;
+}
+
+select {
+ border 2px solid violet;
+ background-color: blue;
+ border-radius: 10px;
+ color: white;
+ margin: 10px;
+ padding: 10px 40px;
+ font-size: 20px;
+}
+
+label {
+ padding: 10px;
+ margin: 10px;
+}
+
+li {
+ font-size: 20px;
+}
+
+.pros {
+ border 2px solid green;
+ border-radius: 10px;
+ background-color: MediumSeaGreen;
+ margin: 10px;
+ opacity: 0.7;
+}
+
+.cons {
+ border 2px solid green;
+ border-radius: 10px;
+ background-color: Tomato;
+ margin: 10px;
+ opacity: 0.7;
+}
+
+.listHeader {
+ margin: auto;
+ padding: 20px;
+}
+
+.center {
+ margin: auto;
+ top: 50%;
+ width: 100%;
+ text-align: center;
+}
+
+#loginForm {
+ position: relative;
+ padding 40px;
+ margin 80px;
+}
+
+form {
+ position: relative;
+ padding: 40px;
+}
+
+.loginButton {
+ background-color: blue;
+ color: white;
+ border: 2px solid violet;
+ border-radius: 20px;
+ height 100px;
+ width: 50%;
+ padding: 10px;
+ font-size: 16px;
+}
diff --git a/Resources/Views/loggedIn.leaf b/Resources/Views/loggedIn.leaf
new file mode 100644
index 0000000..97b1e78
--- /dev/null
+++ b/Resources/Views/loggedIn.leaf
@@ -0,0 +1,43 @@
+#extend("main"):
+ #export("body"):
+
+
Welcome #capitalized(name)
+
+ Please add your pro's and cons during the talk to the list below.
+
+
+ You can add as many pros and cons as you would like.
+
+
+
+
+
+
+ #for(item in pros):
+ - #(item.description)
+ #endfor
+
+
+
+
+
+ #for(item in cons):
+ - #(item.description)
+ #endfor
+
+
+ #endexport
+#endextend
diff --git a/Resources/Views/login.leaf b/Resources/Views/login.leaf
new file mode 100644
index 0000000..eacba9a
--- /dev/null
+++ b/Resources/Views/login.leaf
@@ -0,0 +1,16 @@
+#extend("main"):
+ #export("body"):
+
+
Welcome to chiller Pro vs. Cons
+
Enter your display name below to get started!
+
+
+ #endexport
+#endextend
diff --git a/Resources/Views/main.leaf b/Resources/Views/main.leaf
new file mode 100644
index 0000000..01a0a67
--- /dev/null
+++ b/Resources/Views/main.leaf
@@ -0,0 +1,15 @@
+
+
+
+ Chiller Pro vs. Cons
+
+
+
+
+
+ #import("body")
+
+
+
diff --git a/Sources/App/Controllers/ApiController.swift b/Sources/App/Controllers/ApiController.swift
new file mode 100644
index 0000000..476b3a2
--- /dev/null
+++ b/Sources/App/Controllers/ApiController.swift
@@ -0,0 +1,49 @@
+import Fluent
+import Foundation
+import Vapor
+
+struct ApiController: RouteCollection {
+
+ func boot(routes: any RoutesBuilder) throws {
+ let users = routes.grouped("api", "users")
+ users.get(use: usersIndex(req:))
+ users.post(use: createUser(req:))
+
+ let proCon = routes.grouped("api", "procons")
+ proCon.get(use: prosAndConsIndex(req:))
+ proCon.post(use: createProCon(req:))
+ }
+
+ @Sendable
+ func usersIndex(req: Request) async throws -> [User] {
+ try await User.query(on: req.db).all()
+ }
+
+ @Sendable
+ func prosAndConsIndex(req: Request) async throws -> [ProCon] {
+ try await ProCon.query(on: req.db).all()
+ }
+
+ @Sendable
+ func createUser(req: Request) async throws -> User {
+ let user = try req.content.decode(User.self)
+ try await user.save(on: req.db)
+ return user
+ }
+
+ @Sendable
+ func createProCon(req: Request) async throws -> ProCon {
+ let proconData = try req.content.decode(ProConDTO.self)
+ let proCon = ProCon(type: proconData.type, description: proconData.description, userId: proconData.userId)
+ try await proCon.create(on: req.db)
+ return proCon
+ }
+
+}
+
+struct ProConDTO: Content {
+ let id: UUID?
+ let type: ProCon.ProConType
+ let description: String
+ let userId: User.IDValue
+}
diff --git a/Sources/App/Migrations/CreateProCon.swift b/Sources/App/Migrations/CreateProCon.swift
new file mode 100644
index 0000000..62568c4
--- /dev/null
+++ b/Sources/App/Migrations/CreateProCon.swift
@@ -0,0 +1,19 @@
+import Fluent
+import Vapor
+
+struct CreateProCon: AsyncMigration {
+
+ func prepare(on database: Database) async throws {
+ try await database.schema("procon")
+ .id()
+ .field("description", .string, .required)
+ .field("type", .string, .required)
+ .field("userId", .uuid, .required, .references("user", "id"))
+ .create()
+ }
+
+ func revert(on database: Database) async throws {
+ try await database.schema("procon").delete()
+ }
+
+}
diff --git a/Sources/App/Migrations/CreateUser.swift b/Sources/App/Migrations/CreateUser.swift
new file mode 100644
index 0000000..da7528f
--- /dev/null
+++ b/Sources/App/Migrations/CreateUser.swift
@@ -0,0 +1,16 @@
+import Fluent
+import Vapor
+
+struct CreateUser: AsyncMigration {
+ func prepare(on database: Database) async throws {
+ try await database.schema("user")
+ .id()
+ .field("displayName", .string, .required)
+ .create()
+ }
+
+ func revert(on database: Database) async throws {
+ try await database.schema("user").delete()
+ }
+
+}
diff --git a/Sources/App/Models/BadWords.swift b/Sources/App/Models/BadWords.swift
new file mode 100644
index 0000000..8e0a8bf
--- /dev/null
+++ b/Sources/App/Models/BadWords.swift
@@ -0,0 +1,1039 @@
+func checkForBadWords(in string: String) throws {
+ if badWords.contains(string) {
+ throw BadWordError()
+ } else if string.contains(" ") {
+ let parts = string.split(separator: " ")
+ for part in parts {
+ if badWords.contains(String(part)) {
+ throw BadWordError()
+ }
+ }
+ }
+}
+
+struct BadWordError: Error {}
+
+let badWords: [String] = [
+ "puppy",
+ "2g1c",
+ "a-hole",
+ "a-holes",
+ "abeed",
+ "acrotomophilia",
+ "ahole",
+ "aholes",
+ "alabama hot pocket",
+ "alaskan pipeline",
+ "anal",
+ "anally",
+ "anilingus",
+ "anus",
+ "ape",
+ "apeshit",
+ "arse",
+ "arse-licker",
+ "arse-lickers",
+ "arsed",
+ "arsehole",
+ "arseholed",
+ "arseholes",
+ "arselick",
+ "arselicked",
+ "arselicker",
+ "arselickers",
+ "arselicking",
+ "arselicks",
+ "arses",
+ "arsewipe",
+ "arsewipes",
+ "arsey",
+ "arsing",
+ "ass",
+ "assed",
+ "asses",
+ "asshole",
+ "assholes",
+ "assman",
+ "assmunch",
+ "asswipe",
+ "asswipes",
+ "auto erotic",
+ "autoerotic",
+ "babeland",
+ "baby batter",
+ "baby juice",
+ "ball gag",
+ "ball gravy",
+ "ball kicking",
+ "ball licking",
+ "ball sack",
+ "ball sacks",
+ "ball sucking",
+ "ball-ache",
+ "ballsack",
+ "ballsacks",
+ "bampot",
+ "bampots",
+ "bang",
+ "bangbox",
+ "bangbros",
+ "bangbus",
+ "bareback",
+ "barely legal",
+ "barenaked",
+ "barmpot",
+ "barmpots",
+ "bastard",
+ "bastinado",
+ "batty boy",
+ "batty bwoy",
+ "batty man",
+ "bbw",
+ "bdsm",
+ "bean queen",
+ "beaner",
+ "beaners",
+ "beastiality",
+ "beautiful pussy",
+ "beaver cleaver",
+ "beaver lips",
+ "beef curtain",
+ "beef curtains",
+ "bellend",
+ "bellends",
+ "bender",
+ "bestiality",
+ "biatch",
+ "biatches",
+ "bigass",
+ "bimbos",
+ "bint",
+ "bints",
+ "biotch",
+ "biotches",
+ "birdlock",
+ "bitch",
+ "bitches",
+ "bitchin",
+ "bitchiness",
+ "bitchy",
+ "black cock",
+ "blacky",
+ "blonde action",
+ "blonde on blonde action",
+ "bloodclat",
+ "blow a load",
+ "blow job",
+ "blow jobs",
+ "blow your load",
+ "blowjob",
+ "blowjobs",
+ "blue waffle",
+ "blumpkin",
+ "bollock",
+ "bollocks",
+ "bollox",
+ "bolloxed",
+ "bondage",
+ "boner",
+ "bonk",
+ "boob",
+ "boobies",
+ "boobs",
+ "booty call",
+ "brazzer",
+ "brazzers",
+ "breasts",
+ "brown showers",
+ "brunette action",
+ "bufu",
+ "bugger",
+ "buggered",
+ "buggering",
+ "buggers",
+ "bukake",
+ "bukkake",
+ "bulldyke",
+ "bullet vibe",
+ "bullshat",
+ "bullshit",
+ "bullshits",
+ "bullshitted",
+ "bullshitter",
+ "bullshitters",
+ "bullshitting",
+ "bum",
+ "bumblefuck",
+ "bung hole",
+ "bunghole",
+ "busty",
+ "butt",
+ "butt plug",
+ "buttfuck",
+ "buttfucked",
+ "buttfucker",
+ "buttfuckers",
+ "butthole",
+ "buttplug",
+ "cam girl",
+ "cam girls",
+ "camel jockey",
+ "camel jockeys",
+ "camel toe",
+ "camel toes",
+ "cameltoe",
+ "cameltoes",
+ "camgirl",
+ "camgirls",
+ "camslut",
+ "camsluts",
+ "camwhore",
+ "camwhores",
+ "carpet muncher",
+ "carpet munching",
+ "carpetmuncher",
+ "chi chi bwoy",
+ "chi chi man",
+ "chinaman",
+ "chinamen",
+ "chingchong",
+ "chink",
+ "chinkies",
+ "chinky",
+ "choad",
+ "chocha",
+ "chocolate rosebuds",
+ "chocolate starfish",
+ "chode",
+ "chodes",
+ "cholo",
+ "chutiya",
+ "cialis",
+ "circle jerk",
+ "circle jerking",
+ "circle jerks",
+ "circlejerk",
+ "cleveland steamer",
+ "cleveland steamers",
+ "clit",
+ "clitoris",
+ "clits",
+ "clitty",
+ "clover clamps",
+ "clunge",
+ "clunges",
+ "clusterfuck",
+ "clusterfucks",
+ "cock",
+ "cockface",
+ "cockhead",
+ "cocks",
+ "cocksucker",
+ "cocksuckers",
+ "cocksucking",
+ "commie",
+ "commies",
+ "coolie",
+ "coon",
+ "coons",
+ "coprolagnia",
+ "coprophilia",
+ "cornhole",
+ "crap",
+ "creampie",
+ "cuck",
+ "cucked",
+ "cucking",
+ "cuckold",
+ "cucks",
+ "culchie",
+ "culchies",
+ "cum",
+ "cum dump",
+ "cumdump",
+ "cumming",
+ "cums",
+ "cumshot",
+ "cumshots",
+ "cumslut",
+ "cunnies",
+ "cunnilingus",
+ "cunny",
+ "cunt",
+ "cunt bag",
+ "cunt bags",
+ "cuntbag",
+ "cunted",
+ "cunties",
+ "cunting",
+ "cunts",
+ "cuntsicle",
+ "cunty",
+ "dago",
+ "dammit",
+ "damn",
+ "damnedest",
+ "damnit",
+ "darkie",
+ "darky",
+ "date rape",
+ "daterape",
+ "deadshit",
+ "deep throat",
+ "deepthroat",
+ "dendrophilia",
+ "dick",
+ "dicked",
+ "dickhead",
+ "dickheads",
+ "dicking",
+ "dicks",
+ "dicksplat",
+ "diddle",
+ "dildo",
+ "dildoes",
+ "dildos",
+ "dilligaf",
+ "dingleberries",
+ "dingleberry",
+ "dipshit",
+ "dipshits",
+ "dirty pillows",
+ "dirty sanchez",
+ "dog style",
+ "doggie style",
+ "doggiestyle",
+ "doggy style",
+ "doggystyle",
+ "dogshit",
+ "dogshits",
+ "dolcett",
+ "dominatrix",
+ "dommes",
+ "donkey punch",
+ "dothead",
+ "double dong",
+ "double penetration",
+ "dp action",
+ "dry hump",
+ "dumass",
+ "dumbass",
+ "dumbasses",
+ "dumbshit",
+ "dvda",
+ "dyke",
+ "ecchi",
+ "ejaculation",
+ "erectoplasm",
+ "erotic",
+ "erotism",
+ "escort",
+ "eunuch",
+ "f*ck",
+ "f*cked",
+ "f*cking",
+ "f*cks",
+ "facefuck",
+ "facefucks",
+ "fag",
+ "faggot",
+ "faggotry",
+ "faggots",
+ "faggoty",
+ "fagot",
+ "fagots",
+ "fanny",
+ "fap",
+ "fck",
+ "fcking",
+ "fcks",
+ "fcku",
+ "fcuk",
+ "fcuked",
+ "fcuking",
+ "fcuks",
+ "fecal",
+ "feck",
+ "fecked",
+ "fecker",
+ "feckers",
+ "feckin",
+ "fecking",
+ "fecks",
+ "felch",
+ "felcher",
+ "felchers",
+ "felching",
+ "fellate",
+ "fellatio",
+ "feltch",
+ "female squirting",
+ "femdom",
+ "figging",
+ "fingerbang",
+ "fingering",
+ "first orgy",
+ "fisting",
+ "flamer",
+ "fook",
+ "fooked",
+ "fooker",
+ "fookers",
+ "fooking",
+ "fooks",
+ "foot fetish",
+ "footjob",
+ "free nudes",
+ "frotting",
+ "fuc",
+ "fuck",
+ "fuck face",
+ "fuck faces",
+ "fucka",
+ "fuckable",
+ "fuckbitch",
+ "fuckbitches",
+ "fuckbook",
+ "fuckbooking",
+ "fucke",
+ "fucked",
+ "fuckem",
+ "fucken",
+ "fucker",
+ "fuckers",
+ "fuckery",
+ "fuckface",
+ "fuckfaces",
+ "fuckfeatures",
+ "fuckhead",
+ "fuckheads",
+ "fuckin",
+ "fucking",
+ "fuckmeat",
+ "fuckn",
+ "fucknut",
+ "fucknuts",
+ "fuckoff",
+ "fuckpig",
+ "fuckpigs",
+ "fucks",
+ "fucktard",
+ "fucktards",
+ "fuckung",
+ "fuckup",
+ "fuckups",
+ "fuckwit",
+ "fuckwits",
+ "fuckyou",
+ "fudge packer",
+ "fudgepacker",
+ "funbags",
+ "fuq",
+ "futanari",
+ "g-spot",
+ "gand",
+ "gang bang",
+ "gangbang",
+ "gash-stabber",
+ "gay sex",
+ "geebag",
+ "genitals",
+ "giant cock",
+ "gin jockey",
+ "girl on",
+ "girl on top",
+ "girls 1 cup",
+ "girls gone wild",
+ "glory hole",
+ "glory holes",
+ "gloryhole",
+ "gloryholes",
+ "go to hell",
+ "goatse",
+ "gobshite",
+ "gobshites",
+ "god damn",
+ "godamnit",
+ "goddammit",
+ "goddamn",
+ "goddamnit",
+ "gokkun",
+ "golden shower",
+ "goo girl",
+ "goodpoop",
+ "gook",
+ "goregasm",
+ "groid",
+ "grope",
+ "groped",
+ "gropes",
+ "groping",
+ "group sex",
+ "gtfo",
+ "guro",
+ "gypsy",
+ "hair pie",
+ "hand job",
+ "hand jobs",
+ "handjob",
+ "handjobs",
+ "hard core",
+ "hard-on",
+ "hardcore",
+ "hardons",
+ "harry palms",
+ "he-she",
+ "he-shes",
+ "hebe",
+ "hentai",
+ "heshe",
+ "heshes",
+ "hoe",
+ "homo",
+ "homoerotic",
+ "homosexual",
+ "honkey",
+ "hooker",
+ "hoor",
+ "hoore",
+ "hor",
+ "horny",
+ "horseshit",
+ "hot carl",
+ "hot chick",
+ "houre",
+ "how to kill",
+ "how to murder",
+ "huge fat",
+ "humping",
+ "hure",
+ "hymie",
+ "incest",
+ "injun",
+ "intercourse",
+ "ities",
+ "jack off",
+ "jacked off",
+ "jackoff",
+ "jackoffs",
+ "jacks off",
+ "jackshit",
+ "jail bait",
+ "jailbait",
+ "jap",
+ "japs",
+ "jelly donut",
+ "jerk off",
+ "jerkoff",
+ "jerkoffs",
+ "jesus freak",
+ "jew",
+ "jigaboo",
+ "jiggaboo",
+ "jiggerboo",
+ "jism",
+ "jiz",
+ "joo",
+ "juggs",
+ "jungle bunny",
+ "kaffir",
+ "kaffirs",
+ "kafir",
+ "kafirs",
+ "kiddie fiddler",
+ "kiddie fiddlers",
+ "kidtoucher",
+ "kike",
+ "kikes",
+ "kill myself",
+ "kill you",
+ "kinbaku",
+ "kinkster",
+ "kinky",
+ "kissass",
+ "kitty puncher",
+ "kkk",
+ "klan",
+ "knobbing",
+ "knobhead",
+ "knobheads",
+ "knockers",
+ "ku kluxer",
+ "ladyboy",
+ "ladyboys",
+ "leather restraint",
+ "leather straight jacket",
+ "lemon party",
+ "lesbo",
+ "lezbo",
+ "lezbos",
+ "lezzer",
+ "lezzers",
+ "lezzie",
+ "lezzies",
+ "livesex",
+ "lolita",
+ "lovemaking",
+ "make me come",
+ "male squirting",
+ "mancock",
+ "massive facial",
+ "masterbate",
+ "masterbated",
+ "masterbates",
+ "masterbating",
+ "masterbation",
+ "masturbate",
+ "masturbated",
+ "masturbates",
+ "masturbating",
+ "masturbation",
+ "meat curtains",
+ "menage a trois",
+ "merkin",
+ "merkins",
+ "microshite",
+ "milf",
+ "mingers",
+ "minging",
+ "missionary position",
+ "mo fo",
+ "mofo",
+ "mofos",
+ "molest",
+ "mong",
+ "mongoloid",
+ "mongs",
+ "moose knuckle",
+ "mothafucker",
+ "mothafuckers",
+ "motherfucker",
+ "motherfuckers",
+ "motherfucking",
+ "motherfukkers",
+ "mound of venus",
+ "mr hands",
+ "muff",
+ "muff diver",
+ "muffdiving",
+ "mutha",
+ "muthafucka",
+ "naked chick",
+ "nambla",
+ "nasty wife",
+ "nawashi",
+ "negro",
+ "nig",
+ "nig nog",
+ "nigg",
+ "nigga",
+ "niggas",
+ "nigger",
+ "niggers",
+ "nigs",
+ "nipple",
+ "nipples",
+ "nob-end",
+ "nob-ends",
+ "nobhead",
+ "nobheads",
+ "norks",
+ "nude",
+ "nudity",
+ "numbnuts",
+ "numbnutses",
+ "numpty",
+ "nutsack",
+ "nutten",
+ "nympho",
+ "nymphomania",
+ "nymphos",
+ "octopussy",
+ "omfg",
+ "omorashi",
+ "one cup two girls",
+ "one guy one jar",
+ "orgasm",
+ "orgies",
+ "orgy",
+ "orgys",
+ "oven dodger",
+ "packi",
+ "paedophile",
+ "paki",
+ "pakis",
+ "paleface",
+ "palefaces",
+ "pancake face",
+ "panties",
+ "panty",
+ "pecker",
+ "pederast",
+ "pederasts",
+ "pedo",
+ "pedobear",
+ "pedophile",
+ "pedos",
+ "pegging",
+ "penis",
+ "peter puffer",
+ "phone sex",
+ "phuc",
+ "piece of shit",
+ "pigshit",
+ "pikey",
+ "pillow biter",
+ "pillow biters",
+ "piss",
+ "piss pig",
+ "pissflaps",
+ "pisshead",
+ "pissheads",
+ "pissing",
+ "pisspig",
+ "playboy",
+ "playmate",
+ "pleasure chest",
+ "pocket pool",
+ "pole licker",
+ "pole smoker",
+ "pole sucker",
+ "polesmoker",
+ "ponyplay",
+ "poo",
+ "poofster",
+ "poofsters",
+ "poofta",
+ "pooftah",
+ "poofter",
+ "poofters",
+ "poontang",
+ "poop",
+ "poop chute",
+ "poopchute",
+ "porch monkey",
+ "porch monkeys",
+ "porchmonkey",
+ "porchmonkeys",
+ "pork sword",
+ "porn",
+ "porno",
+ "pornography",
+ "pornos",
+ "porns",
+ "prick",
+ "pron",
+ "prons",
+ "pubes",
+ "punani",
+ "punany",
+ "pusses",
+ "pussey",
+ "pussy",
+ "pussyboy",
+ "pussyboys",
+ "pussylicker",
+ "pussylickers",
+ "pussyman",
+ "pussymen",
+ "pussys",
+ "puta",
+ "putas",
+ "queaf",
+ "queef",
+ "queefs",
+ "queer",
+ "quim",
+ "quims",
+ "rag head",
+ "rag heads",
+ "raghead",
+ "ragheads",
+ "raging boner",
+ "rape",
+ "raping",
+ "rapist",
+ "rectum",
+ "redskin",
+ "rent boy",
+ "rent boys",
+ "rentboy",
+ "rentboys",
+ "retard",
+ "retarded",
+ "reverse cowgirl",
+ "rim job",
+ "rimjob",
+ "rimming",
+ "rug muncher",
+ "rusty trombone",
+ "s&m",
+ "sadism",
+ "salad tosser",
+ "sambo",
+ "santorum",
+ "sausage jockey",
+ "scat",
+ "schizo",
+ "schlong",
+ "scissoring",
+ "scroat",
+ "scroats",
+ "scrote",
+ "scrotes",
+ "scumbag",
+ "scumbags",
+ "semen",
+ "sex",
+ "sexcam",
+ "sexo",
+ "sexual",
+ "sexuality",
+ "sexually",
+ "sexy",
+ "shaggin",
+ "shat",
+ "shaved pussy",
+ "she-he",
+ "she-hes",
+ "sheep shagger",
+ "sheep shaggers",
+ "shehe",
+ "shehes",
+ "shemale",
+ "shemales",
+ "shibari",
+ "shiester",
+ "shirtlifter",
+ "shirtlifters",
+ "shit",
+ "shitbag",
+ "shitbags",
+ "shitblimp",
+ "shitbox",
+ "shitboxes",
+ "shite",
+ "shited",
+ "shitehawk",
+ "shitehawks",
+ "shiter",
+ "shiters",
+ "shites",
+ "shitey",
+ "shitface",
+ "shitfaced",
+ "shithead",
+ "shitheads",
+ "shithole",
+ "shitholes",
+ "shithouse",
+ "shithouses",
+ "shiting",
+ "shitless",
+ "shitload",
+ "shitloads",
+ "shits",
+ "shitstain",
+ "shitstains",
+ "shitted",
+ "shitter",
+ "shitters",
+ "shittier",
+ "shittiest",
+ "shitting",
+ "shitty",
+ "shity",
+ "shlong",
+ "shlongs",
+ "shota",
+ "shrimping",
+ "shut the hell up",
+ "shut up",
+ "shyt",
+ "shyte",
+ "skank",
+ "skanks",
+ "skanky",
+ "skeet",
+ "slant eye",
+ "slanteye",
+ "slaphead",
+ "slich",
+ "slit",
+ "slut",
+ "sluts",
+ "slutty",
+ "smackhead",
+ "smackheads",
+ "smart-arse",
+ "smart-ass",
+ "smartarse",
+ "smartass",
+ "smartasse",
+ "smartasses",
+ "smeg",
+ "smegma",
+ "smut",
+ "snatch",
+ "snow bunny",
+ "snowballing",
+ "sodomize",
+ "sodomy",
+ "sonofabitch",
+ "spaff",
+ "spaffed",
+ "spaffing",
+ "spaffs",
+ "spastic",
+ "spic",
+ "spick",
+ "spicks",
+ "spics",
+ "spik",
+ "spiks",
+ "spiv",
+ "splooge",
+ "splooge moose",
+ "spooge",
+ "spook",
+ "spread legs",
+ "spunk",
+ "stfu",
+ "stiffies",
+ "stiffy",
+ "strap on",
+ "strapon",
+ "strip club",
+ "style doggy",
+ "suck",
+ "sucks",
+ "sultry women",
+ "swastika",
+ "swinger",
+ "tacohead",
+ "tadger",
+ "tadgers",
+ "tainted love",
+ "tallywhacker",
+ "tallywhackers",
+ "tar-baby",
+ "tard",
+ "tards",
+ "taste my",
+ "tata",
+ "tatas",
+ "tea bagging",
+ "threesome",
+ "throat yogurt",
+ "throater",
+ "throating",
+ "thumbzilla",
+ "thundercunt",
+ "tied up",
+ "tight white",
+ "tit",
+ "tits",
+ "tittie",
+ "titties",
+ "titty",
+ "tittys",
+ "todger",
+ "todgers",
+ "tongue in a",
+ "tosser",
+ "tossing salad",
+ "towelhead",
+ "tramp",
+ "trannies",
+ "tranny",
+ "transvestite",
+ "tribadism",
+ "trouser snake",
+ "tub girl",
+ "tubgirl",
+ "turd",
+ "tushy",
+ "twat",
+ "twathead",
+ "twats",
+ "twatties",
+ "twatty",
+ "twink",
+ "twinkie",
+ "two girls one cup",
+ "twot",
+ "twots",
+ "twunt",
+ "twunts",
+ "undressing",
+ "upskirt",
+ "upskirts",
+ "urethra play",
+ "urophilia",
+ "vadge",
+ "vadges",
+ "vag",
+ "vagina",
+ "venus mound",
+ "viagra",
+ "vibrator",
+ "violet wand",
+ "vorarephilia",
+ "voyeur",
+ "voyeurweb",
+ "voyuer",
+ "vulva",
+ "wang",
+ "wanger",
+ "wangers",
+ "wank",
+ "wanked",
+ "wanker",
+ "wankers",
+ "wanking",
+ "wanks",
+ "wankstain",
+ "wankstains",
+ "wazz",
+ "wazzock",
+ "wazzocks",
+ "wet dream",
+ "wetback",
+ "wetbacks",
+ "whack job",
+ "whack off",
+ "whacked off",
+ "whacking off",
+ "whacks off",
+ "white power",
+ "whore",
+ "whore house",
+ "whored",
+ "whoredom",
+ "whorehouse",
+ "whores",
+ "whoring",
+ "whorish",
+ "wigger",
+ "wiggers",
+ "willy-whacker",
+ "window licker",
+ "wog",
+ "wogs",
+ "wooftah",
+ "woofter",
+ "woofters",
+ "wop",
+ "wops",
+ "worldsex",
+ "wrapping men",
+ "yam-yam",
+ "yaoi",
+ "yid",
+ "yids",
+ "yiffy",
+ "zipperhead",
+ "zipperheads",
+ "zoophilia",
+ "🖕"
+]
diff --git a/Sources/App/Models/ProCon.swift b/Sources/App/Models/ProCon.swift
new file mode 100644
index 0000000..105e2d1
--- /dev/null
+++ b/Sources/App/Models/ProCon.swift
@@ -0,0 +1,40 @@
+import Fluent
+import Foundation
+import Vapor
+
+final class ProCon: Model {
+ static let schema = "procon"
+
+ @ID(key: .id)
+ var id: UUID?
+
+ @Parent(key: "userId")
+ var user: User
+
+ @Field(key: "description")
+ var description: String
+
+ @Field(key: "type")
+ var type: ProConType
+
+ init() {}
+
+ init(
+ id: UUID? = nil,
+ type: ProConType,
+ description: String,
+ userId: User.IDValue
+ ) {
+ self.id = id
+ self.type = type
+ self.description = description
+ $user.id = userId
+ }
+
+ enum ProConType: String, Codable, Equatable, Sendable {
+ case pro, con
+ }
+
+}
+
+extension ProCon: Content {}
diff --git a/Sources/App/Models/User.swift b/Sources/App/Models/User.swift
new file mode 100644
index 0000000..92c9a63
--- /dev/null
+++ b/Sources/App/Models/User.swift
@@ -0,0 +1,25 @@
+import Fluent
+import Foundation
+import Vapor
+
+final class User: Model {
+ static let schema = "user"
+
+ @ID(key: .id)
+ var id: UUID?
+
+ @Field(key: "displayName")
+ var displayName: String
+
+ @Children(for: \.$user)
+ var prosAndCons: [ProCon]
+
+ init() {}
+
+ init(id: UUID? = nil, displayName: String) {
+ self.id = id
+ self.displayName = displayName
+ }
+}
+
+extension User: Content {}
diff --git a/Sources/App/configure.swift b/Sources/App/configure.swift
index b84cb15..16376f8 100644
--- a/Sources/App/configure.swift
+++ b/Sources/App/configure.swift
@@ -1,9 +1,22 @@
+import Fluent
+import FluentSQLiteDriver
+import Leaf
import Vapor
// configures your application
public func configure(_ app: Application) async throws {
- // uncomment to serve files from /Public folder
- // app.middleware.use(FileMiddleware(publicDirectory: app.directory.publicDirectory))
- // register routes
- try routes(app)
+ // uncomment to serve files from /Public folder
+ app.middleware.use(FileMiddleware(publicDirectory: app.directory.publicDirectory))
+ app.middleware.use(app.sessions.middleware)
+ // app.databases.use(.sqlite(.file("db.sqlite")), as: .sqlite)
+ app.databases.use(.sqlite(.memory), as: .sqlite)
+ app.migrations.add(CreateProCon())
+ app.migrations.add(CreateUser())
+ app.views.use(.leaf)
+
+ // register routes
+ try routes(app)
+ try app.register(collection: ApiController())
+
+ try await app.autoMigrate()
}
diff --git a/Sources/App/entrypoint.swift b/Sources/App/entrypoint.swift
index 2e85ece..846c5b3 100644
--- a/Sources/App/entrypoint.swift
+++ b/Sources/App/entrypoint.swift
@@ -1,31 +1,31 @@
-import Vapor
import Logging
import NIOCore
import NIOPosix
+import Vapor
@main
enum Entrypoint {
- static func main() async throws {
- var env = try Environment.detect()
- try LoggingSystem.bootstrap(from: &env)
-
- let app = try await Application.make(env)
+ static func main() async throws {
+ var env = try Environment.detect()
+ try LoggingSystem.bootstrap(from: &env)
- // This attempts to install NIO as the Swift Concurrency global executor.
- // You can enable it if you'd like to reduce the amount of context switching between NIO and Swift Concurrency.
- // Note: this has caused issues with some libraries that use `.wait()` and cleanly shutting down.
- // If enabled, you should be careful about calling async functions before this point as it can cause assertion failures.
- // let executorTakeoverSuccess = NIOSingletons.unsafeTryInstallSingletonPosixEventLoopGroupAsConcurrencyGlobalExecutor()
- // app.logger.debug("Tried to install SwiftNIO's EventLoopGroup as Swift's global concurrency executor", metadata: ["success": .stringConvertible(executorTakeoverSuccess)])
-
- do {
- try await configure(app)
- } catch {
- app.logger.report(error: error)
- try? await app.asyncShutdown()
- throw error
- }
- try await app.execute()
- try await app.asyncShutdown()
+ let app = try await Application.make(env)
+
+ // This attempts to install NIO as the Swift Concurrency global executor.
+ // You can enable it if you'd like to reduce the amount of context switching between NIO and Swift Concurrency.
+ // Note: this has caused issues with some libraries that use `.wait()` and cleanly shutting down.
+ // If enabled, you should be careful about calling async functions before this point as it can cause assertion failures.
+ // let executorTakeoverSuccess = NIOSingletons.unsafeTryInstallSingletonPosixEventLoopGroupAsConcurrencyGlobalExecutor()
+ // app.logger.debug("Tried to install SwiftNIO's EventLoopGroup as Swift's global concurrency executor", metadata: ["success": .stringConvertible(executorTakeoverSuccess)])
+
+ do {
+ try await configure(app)
+ } catch {
+ app.logger.report(error: error)
+ try? await app.asyncShutdown()
+ throw error
}
+ try await app.execute()
+ try await app.asyncShutdown()
+ }
}
diff --git a/Sources/App/routes.swift b/Sources/App/routes.swift
index 2edcc8f..e7d6ba9 100644
--- a/Sources/App/routes.swift
+++ b/Sources/App/routes.swift
@@ -1,11 +1,93 @@
+import Fluent
+import Foundation
import Vapor
func routes(_ app: Application) throws {
- app.get { req async in
- "It works!"
+ app.get { req in
+ let output = try await req.view.render("login")
+ return output
+ }
+
+ app.get("loggedIn") { req in
+ guard let userIdString = req.session.data["userId"],
+ let displayName = req.session.data["displayName"],
+ let userId = UUID(uuidString: userIdString)
+ else {
+ return try await req.view.render("/")
+ }
+ guard let user = try await User.query(on: req.db)
+ .filter(\.$id == userId)
+ .with(\.$prosAndCons)
+ .first()
+ else {
+ throw Abort(.unauthorized)
+ }
+ // let prosAndCons = try await user.$prosAndCons.get(on: req.db)
+ return try await req.view.render(
+ "loggedIn",
+ LoggedInContext(name: displayName, prosAndCons: user.prosAndCons)
+ )
+ }
+
+ app.get("submitProOrCon") { req in
+ let params = try req.query.decode(SubmitProOrCon.self)
+ guard let userIdString = req.session.data["userId"],
+ let userId = UUID(uuidString: userIdString)
+ else {
+ throw Abort(.unauthorized)
+ }
+ let proOrCon = ProCon(type: params.type, description: params.description, userId: userId)
+ _ = try await req.db.transaction {
+ proOrCon.save(on: $0)
+ }
+ .get()
+
+ return req.redirect(to: "loggedIn")
+ }
+
+ app.get("login") { req in
+ let params = try req.query.decode(LoginParams.self)
+ req.logger.info("params: \(params)")
+
+ do {
+ try checkForBadWords(in: params.displayName)
+ } catch {
+ throw Abort(.unauthorized, reason: "Stop using such naughty language.")
}
- app.get("hello") { req async -> String in
- "Hello, world!"
- }
+ let user = User(displayName: params.displayName)
+ _ = try await req.db.transaction {
+ user.save(on: $0)
+ }.get()
+
+ let userId = user.id?.uuidString ?? "nil"
+ req.session.data["userId"] = userId
+ req.session.data["displayName"] = user.displayName
+
+ // return try await req.view.render("loggedIn", ["name": user.displayName])
+ return req.redirect(to: "loggedIn")
+ }
+}
+
+struct DisplayNameError: Error {}
+
+struct LoginParams: Content {
+ let displayName: String
+}
+
+struct SubmitProOrCon: Content {
+ let type: ProCon.ProConType
+ let description: String
+}
+
+struct LoggedInContext: Encodable {
+ let name: String
+ let pros: [ProCon]
+ let cons: [ProCon]
+
+ init(name: String, prosAndCons: [ProCon]) {
+ self.name = name
+ self.cons = prosAndCons.filter { $0.type == .con }
+ self.pros = prosAndCons.filter { $0.type == .pro }
+ }
}