From 8dba393267442e7ad7f2b902ba5d30881a6dc104 Mon Sep 17 00:00:00 2001 From: Michael Housh Date: Tue, 31 Dec 2024 17:02:29 -0500 Subject: [PATCH] feat: Initial commit --- .editorconfig | 7 + .nvim/xcodebuild/devices.json | 1 + .nvim/xcodebuild/settings.json | 1 + .swiftformat | 11 + .swiftlint.yml | 11 + Package.resolved | 294 ++++++ Package.swift | 70 +- Public/js/site.js | 35 + Public/styles/site.css | 95 ++ Resources/Views/loggedIn.leaf | 43 + Resources/Views/login.leaf | 16 + Resources/Views/main.leaf | 15 + Sources/App/Controllers/ApiController.swift | 49 + Sources/App/Migrations/CreateProCon.swift | 19 + Sources/App/Migrations/CreateUser.swift | 16 + Sources/App/Models/BadWords.swift | 1039 +++++++++++++++++++ Sources/App/Models/ProCon.swift | 40 + Sources/App/Models/User.swift | 25 + Sources/App/configure.swift | 21 +- Sources/App/entrypoint.swift | 44 +- Sources/App/routes.swift | 92 +- 21 files changed, 1881 insertions(+), 63 deletions(-) create mode 100644 .editorconfig create mode 100644 .nvim/xcodebuild/devices.json create mode 100644 .nvim/xcodebuild/settings.json create mode 100644 .swiftformat create mode 100644 .swiftlint.yml create mode 100644 Package.resolved create mode 100644 Public/js/site.js create mode 100644 Public/styles/site.css create mode 100644 Resources/Views/loggedIn.leaf create mode 100644 Resources/Views/login.leaf create mode 100644 Resources/Views/main.leaf create mode 100644 Sources/App/Controllers/ApiController.swift create mode 100644 Sources/App/Migrations/CreateProCon.swift create mode 100644 Sources/App/Migrations/CreateUser.swift create mode 100644 Sources/App/Models/BadWords.swift create mode 100644 Sources/App/Models/ProCon.swift create mode 100644 Sources/App/Models/User.swift 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. +

+
+ +
+
+ +
+ +
+
+ +
+
+
+

Pros - Count: #count(pros)

+ +
+
+

Cons - Count: #count(cons)

+ +
+ #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 + + + + +

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 } + } }