Skip to content

Commit c64cfaa

Browse files
committed
Finish main app structure
1 parent 38d205d commit c64cfaa

14 files changed

+557
-4
lines changed

Config/app.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
"foo": "foo-bar"
2+
"updatePassword": "$UPDATE_PASSWORD"
33
}

Config/development/database.json

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"postgres": {
3+
"host": "localhost",
4+
"port": 5432,
5+
"dbname": "passcards",
6+
"user": "$USER",
7+
"password": ""
8+
}
9+
}

Config/production/app.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
22
"key": "$VAPOR_APP_KEY"
3-
}
3+
}

Config/production/database.json

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"postgres": {
3+
"host": "$PG_HOST",
4+
"port": "$PG_PORT"",
5+
"dbname": "$PG_DBNAME",
6+
"user": "$PG_USER",
7+
"password": "$PG_PASSWORD"
8+
}
9+
}

Config/storage.json

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"driver": "s3",
3+
"bucket": "$S3_BUCKET",
4+
"accessKey": "$S3_ACCESS_KEY",
5+
"secretKey": "$S3_SECRET_KEY",
6+
"host": "s3.amazonaws.com",
7+
"region": "$S3_REGION",
8+
"template": "/#file"
9+
}

Package.pins

+42
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,24 @@
11
{
22
"autoPin": true,
33
"pins": [
4+
{
5+
"package": "AWS",
6+
"reason": null,
7+
"repositoryURL": "https://github.com/nodes-vapor/aws.git",
8+
"version": "0.1.0"
9+
},
410
{
511
"package": "CLibreSSL",
612
"reason": null,
713
"repositoryURL": "https://github.com/vapor/clibressl.git",
814
"version": "1.0.0"
915
},
16+
{
17+
"package": "CPostgreSQL",
18+
"reason": null,
19+
"repositoryURL": "https://github.com/vapor/cpostgresql.git",
20+
"version": "1.0.0"
21+
},
1022
{
1123
"package": "Console",
1224
"reason": null,
@@ -25,6 +37,12 @@
2537
"repositoryURL": "https://github.com/vapor/crypto.git",
2638
"version": "1.1.0"
2739
},
40+
{
41+
"package": "DataURI",
42+
"reason": null,
43+
"repositoryURL": "https://github.com/nodes-vapor/data-uri.git",
44+
"version": "0.1.1"
45+
},
2846
{
2947
"package": "Engine",
3048
"reason": null,
@@ -37,6 +55,12 @@
3755
"repositoryURL": "https://github.com/vapor/fluent.git",
3856
"version": "1.4.3"
3957
},
58+
{
59+
"package": "FluentPostgreSQL",
60+
"reason": null,
61+
"repositoryURL": "https://github.com/vapor-community/postgresql-driver.git",
62+
"version": "1.1.0"
63+
},
4064
{
4165
"package": "JSON",
4266
"reason": null,
@@ -55,6 +79,12 @@
5579
"repositoryURL": "https://github.com/vapor/leaf.git",
5680
"version": "1.0.7"
5781
},
82+
{
83+
"package": "MimeLib",
84+
"reason": null,
85+
"repositoryURL": "https://github.com/manGoweb/MimeLib.git",
86+
"version": "1.0.0"
87+
},
5888
{
5989
"package": "Multipart",
6090
"reason": null,
@@ -79,6 +109,12 @@
79109
"repositoryURL": "https://github.com/vapor/polymorphic.git",
80110
"version": "1.0.1"
81111
},
112+
{
113+
"package": "PostgreSQL",
114+
"reason": null,
115+
"repositoryURL": "https://github.com/vapor/postgresql.git",
116+
"version": "1.1.0"
117+
},
82118
{
83119
"package": "Routing",
84120
"reason": null,
@@ -91,6 +127,12 @@
91127
"repositoryURL": "https://github.com/vapor/socks.git",
92128
"version": "1.2.7"
93129
},
130+
{
131+
"package": "Storage",
132+
"reason": null,
133+
"repositoryURL": "https://github.com/nodes-vapor/storage.git",
134+
"version": "0.3.7"
135+
},
94136
{
95137
"package": "TLS",
96138
"reason": null,

Package.swift

+4-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ import PackageDescription
33
let package = Package(
44
name: "passcards",
55
dependencies: [
6-
.Package(url: "https://github.com/vapor/vapor.git", majorVersion: 1, minor: 5)
6+
.Package(url: "https://github.com/vapor/vapor.git", majorVersion: 1, minor: 5),
7+
.Package(url: "https://github.com/vapor/fluent.git", majorVersion: 1),
8+
.Package(url: "https://github.com/vapor-community/postgresql-driver.git", majorVersion: 1, minor: 1),
9+
.Package(url: "https://github.com/nodes-vapor/storage.git", majorVersion: 0, minor: 3),
710
],
811
exclude: [
912
"Config",
@@ -13,4 +16,3 @@ let package = Package(
1316
"Resources",
1417
]
1518
)
16-
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
import FormData
2+
import Foundation
3+
import HTTP
4+
import Routing
5+
import Storage
6+
import Vapor
7+
8+
private extension Field {
9+
var data: Bytes {
10+
return part.body
11+
}
12+
13+
var string: String? {
14+
let body = part.body
15+
return body.withUnsafeBufferPointer { buffer in
16+
guard let uptr = buffer.baseAddress else { return nil }
17+
return uptr.withMemoryRebound(to: CChar.self, capacity: body.count, String.init(utf8String:))
18+
}
19+
}
20+
}
21+
22+
final class VanityCollection: RouteCollection {
23+
typealias Wrapped = HTTP.Responder
24+
25+
let updatePassword: String?
26+
27+
init(updatePassword: String?) {
28+
self.updatePassword = updatePassword
29+
}
30+
31+
func isAuthenticated(request: Request) -> Bool {
32+
guard let updatePassword = updatePassword else {
33+
// No password = always authenticated
34+
return true
35+
}
36+
37+
if let authorization = request.headers[.authorization] {
38+
return authorization == "Bearer \(updatePassword)"
39+
} else {
40+
return false
41+
}
42+
}
43+
44+
func findPass(vanityName: String) throws -> Pass? {
45+
return try Pass.query()
46+
.filter("vanity_name", vanityName)
47+
.first()
48+
}
49+
50+
func parseVanityName(from fileName: String) -> String? {
51+
if let suffixRange = fileName.range(of: ".pkpass", options: [.anchored, .backwards, .caseInsensitive]) {
52+
return fileName[fileName.startIndex ..< suffixRange.lowerBound]
53+
} else {
54+
return nil
55+
}
56+
}
57+
58+
func build<B: RouteBuilder>(_ builder: B) where B.Value == Wrapped {
59+
builder.get(String.self) { request, passName in
60+
guard let vanityName = self.parseVanityName(from: passName),
61+
let pass = try self.findPass(vanityName: vanityName),
62+
let passPath = pass.passPath
63+
else {
64+
return Response(status: .notFound)
65+
}
66+
67+
let updatedAt = pass.updatedAt ?? Date()
68+
let headers: [HeaderKey: String] = [
69+
.contentType: "application/vnd.apple.pkpass",
70+
.lastModified: rfc2616DateFormatter.string(from: updatedAt),
71+
]
72+
let passBytes = try Storage.get(path: passPath)
73+
return Response(status: .ok, headers: headers, body: .data(passBytes))
74+
}
75+
76+
builder.post(String.self) { request, passName in
77+
guard self.isAuthenticated(request: request) else {
78+
return Response(status: .unauthorized)
79+
}
80+
81+
guard let vanityName = self.parseVanityName(from: passName) else {
82+
return Response(status: .notFound)
83+
}
84+
85+
guard try self.findPass(vanityName: vanityName) == nil else {
86+
return Response(status: .preconditionFailed)
87+
}
88+
89+
guard let formData = request.formData,
90+
let authenticationToken = formData["authentication_token"]?.string,
91+
let passTypeIdentifier = formData["pass_type_identifier"]?.string,
92+
let serialNumber = formData["serial_number"]?.string,
93+
let passData = formData["pass"]?.data
94+
else {
95+
return Response(status: .badRequest)
96+
}
97+
98+
let passPath = try Storage.upload(bytes: passData, fileName: vanityName, fileExtension: "pkpass", mime: "application/vnd.apple.pkpass")
99+
100+
var pass = Pass()
101+
pass.vanityName = vanityName
102+
pass.authenticationToken = authenticationToken
103+
pass.serialNumber = serialNumber
104+
pass.passTypeIdentifier = passTypeIdentifier
105+
pass.passPath = passPath
106+
pass.updatedAt = Date()
107+
try pass.save()
108+
109+
return Response(status: .created)
110+
}
111+
112+
builder.put(String.self) { request, passName in
113+
guard self.isAuthenticated(request: request) else {
114+
return Response(status: .unauthorized)
115+
}
116+
117+
guard let vanityName = self.parseVanityName(from: passName),
118+
var pass = try self.findPass(vanityName: vanityName)
119+
else {
120+
return Response(status: .notFound)
121+
}
122+
123+
guard let formData = request.formData,
124+
let passData = formData["pass"]?.data
125+
else {
126+
return Response(status: .badRequest)
127+
}
128+
129+
let passPath = try Storage.upload(bytes: passData, fileName: vanityName, fileExtension: "pkpass", mime: "application/vnd.apple.pkpass")
130+
pass.passPath = passPath
131+
pass.updatedAt = Date()
132+
try pass.save()
133+
134+
return Response(status: .seeOther, headers: [.location: String(describing: request.uri)])
135+
}
136+
}
137+
}

0 commit comments

Comments
 (0)