feat: Initial commit
This commit is contained in:
7
.editorconfig
Normal file
7
.editorconfig
Normal file
@@ -0,0 +1,7 @@
|
||||
root = true
|
||||
|
||||
[*.swift]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
tab_width = 2
|
||||
trim_trailing_whitespace = true
|
||||
1
.nvim/xcodebuild/devices.json
Normal file
1
.nvim/xcodebuild/devices.json
Normal file
@@ -0,0 +1 @@
|
||||
{"projectFile": "/Volumes/Bucket/Repos/hello/Package.swift", "scheme": "hello", "devices": [{"id": "00008103-000E79D011EA001E", "name": "My Mac", "arch": "arm64e", "platform": "macOS"}]}
|
||||
1
.nvim/xcodebuild/settings.json
Normal file
1
.nvim/xcodebuild/settings.json
Normal file
@@ -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"}
|
||||
11
.swiftformat
Normal file
11
.swiftformat
Normal file
@@ -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
|
||||
11
.swiftlint.yml
Normal file
11
.swiftlint.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
disabled_rules:
|
||||
- closing_brace
|
||||
- fuction_body_length
|
||||
- opening_brace
|
||||
- nesting
|
||||
|
||||
included:
|
||||
- Sources
|
||||
- Tests
|
||||
|
||||
ignore_multiline_statement_conditions: true
|
||||
294
Package.resolved
Normal file
294
Package.resolved
Normal file
@@ -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
|
||||
}
|
||||
@@ -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")
|
||||
] }
|
||||
|
||||
35
Public/js/site.js
Normal file
35
Public/js/site.js
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
95
Public/styles/site.css
Normal file
95
Public/styles/site.css
Normal file
@@ -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;
|
||||
}
|
||||
43
Resources/Views/loggedIn.leaf
Normal file
43
Resources/Views/loggedIn.leaf
Normal file
@@ -0,0 +1,43 @@
|
||||
#extend("main"):
|
||||
#export("body"):
|
||||
<div class="center">
|
||||
<h1>Welcome #capitalized(name)</h1>
|
||||
<p>
|
||||
Please add your pro's and cons during the talk to the list below.
|
||||
</p>
|
||||
<p>
|
||||
<small>You can add as many pros and cons as you would like.</small>
|
||||
</p>
|
||||
<form id="proconForm" action="submitProOrCon">
|
||||
<select id="type", name="type">
|
||||
<option value="pro">Pro</option>
|
||||
<option value="con">Con</option>
|
||||
</select>
|
||||
<br>
|
||||
<br>
|
||||
<label for="description">Description:</label>
|
||||
<br>
|
||||
<input type="text" id="description" name="description" required>
|
||||
<br>
|
||||
<br>
|
||||
<input class="loginButton" type="submit" value="Submit">
|
||||
</form>
|
||||
</div>
|
||||
<div class="pros">
|
||||
<p class="listHeader">Pros - <small>Count: #count(pros)</small></p>
|
||||
<ul>
|
||||
#for(item in pros):
|
||||
<li>#(item.description)</li>
|
||||
#endfor
|
||||
</ul>
|
||||
</div>
|
||||
<div class="cons">
|
||||
<p class="listHeader">Cons - <small>Count: #count(cons)</small></p>
|
||||
<ul>
|
||||
#for(item in cons):
|
||||
<li>#(item.description)</li>
|
||||
#endfor
|
||||
</ul>
|
||||
</div>
|
||||
#endexport
|
||||
#endextend
|
||||
16
Resources/Views/login.leaf
Normal file
16
Resources/Views/login.leaf
Normal file
@@ -0,0 +1,16 @@
|
||||
#extend("main"):
|
||||
#export("body"):
|
||||
<div class="center">
|
||||
<h1>Welcome to chiller Pro vs. Cons</h1>
|
||||
<p style="font-size: 12px;">Enter your display name below to get started!</p>
|
||||
<form id="loginForm" action="login">
|
||||
<label for="displayName">Display Name:</label>
|
||||
<br>
|
||||
<input type="text" id="displayName" name="displayName" placeholder="Name" required>
|
||||
<br>
|
||||
<br>
|
||||
<input type="submit" value="Submit" class="loginButton">
|
||||
</form>
|
||||
</div>
|
||||
#endexport
|
||||
#endextend
|
||||
15
Resources/Views/main.leaf
Normal file
15
Resources/Views/main.leaf
Normal file
@@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Chiller Pro vs. Cons</title>
|
||||
<link rel="stylesheet" href="styles/site.css">
|
||||
<script src="js/site.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<header><h1>Chiller Pro vs. Cons<h1></header>
|
||||
#import("body")
|
||||
<footer>
|
||||
<span>2025 Symposium</span>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
49
Sources/App/Controllers/ApiController.swift
Normal file
49
Sources/App/Controllers/ApiController.swift
Normal file
@@ -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
|
||||
}
|
||||
19
Sources/App/Migrations/CreateProCon.swift
Normal file
19
Sources/App/Migrations/CreateProCon.swift
Normal file
@@ -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()
|
||||
}
|
||||
|
||||
}
|
||||
16
Sources/App/Migrations/CreateUser.swift
Normal file
16
Sources/App/Migrations/CreateUser.swift
Normal file
@@ -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()
|
||||
}
|
||||
|
||||
}
|
||||
1039
Sources/App/Models/BadWords.swift
Normal file
1039
Sources/App/Models/BadWords.swift
Normal file
File diff suppressed because it is too large
Load Diff
40
Sources/App/Models/ProCon.swift
Normal file
40
Sources/App/Models/ProCon.swift
Normal file
@@ -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 {}
|
||||
25
Sources/App/Models/User.swift
Normal file
25
Sources/App/Models/User.swift
Normal file
@@ -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 {}
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user