├── .gitignore
├── Package.swift
├── README.md
├── Sources
├── Database.swift
├── Handlers.swift
├── URL.swift
└── main.swift
└── webroot
├── index.mustache
├── perfect-2.0.png
└── style.mustache
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## Build generated
6 | build/
7 | DerivedData
8 |
9 | ## Various settings
10 | *.pbxuser
11 | !default.pbxuser
12 | *.mode1v3
13 | !default.mode1v3
14 | *.mode2v3
15 | !default.mode2v3
16 | *.perspectivev3
17 | !default.perspectivev3
18 | xcuserdata
19 |
20 | ## Other
21 | *.xccheckout
22 | *.moved-aside
23 | *.xcuserstate
24 | *.xcscmblueprint
25 |
26 | ## Obj-C/Swift specific
27 | *.hmap
28 | *.ipa
29 |
30 | ## Playgrounds
31 | timeline.xctimeline
32 | playground.xcworkspace
33 |
34 | # Swift Package Manager
35 | #
36 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
37 | # Packages/
38 | .build/
39 |
40 | # CocoaPods
41 | #
42 | # We recommend against adding the Pods directory to your .gitignore. However
43 | # you should judge for yourself, the pros and cons are mentioned at:
44 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
45 | #
46 | # Pods/
47 |
48 | # Carthage
49 | #
50 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
51 | # Carthage/Checkouts
52 |
53 | Carthage/Build
54 |
55 | # fastlane
56 | #
57 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
58 | # screenshots whenever they are needed.
59 | # For more information about the recommended setup visit:
60 | # https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md
61 |
62 | fastlane/report.xml
63 | fastlane/screenshots
64 |
65 | Packages/
66 | *.xcodeproj
67 | url-database
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Package.swift
3 | // Perfect Cookie Demo
4 | //
5 | // Created by Jonathan Guthrie on 2016-09-28.
6 | // Copyright (C) 2015 PerfectlySoft, Inc.
7 | //
8 | //===----------------------------------------------------------------------===//
9 | //
10 | // This source file is part of the Perfect.org open source project
11 | //
12 | // Copyright (c) 2015 - 2016 PerfectlySoft Inc. and the Perfect project authors
13 | // Licensed under Apache License v2.0
14 | //
15 | // See http://perfect.org/licensing.html for license information
16 | //
17 | //===----------------------------------------------------------------------===//
18 | //
19 |
20 | import PackageDescription
21 |
22 | // Note that the following Swift Package Manager dependancy inclusion will also import other required modules.
23 | let package = Package(
24 | name: "PerfectURLShortener",
25 | targets: [],
26 | dependencies: [
27 | .Package(url: "https://github.com/PerfectlySoft/Perfect-SQLite.git", majorVersion: 2, minor: 0),
28 | .Package(url: "https://github.com/PerfectlySoft/Perfect-HTTPServer.git", majorVersion: 2, minor: 0),
29 | .Package(url: "https://github.com/PerfectlySoft/Perfect-Mustache.git", majorVersion: 2, minor: 0),
30 | .Package(url: "https://github.com/iamjono/SwiftString.git",majorVersion: 1, minor: 0),
31 | .Package(url: "https://github.com/iamjono/SwiftRandom.git",majorVersion: 0, minor: 2)
32 | ]
33 | )
34 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Perfect URL Shortener Example
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | An Example URL Shortener System for Perfect
43 |
44 | This project demonstrates the following:
45 |
46 | * Mustache templating
47 | * URL routing and handlers
48 | * URL Variables
49 | * Static file serving
50 | * SQLite3 database setup, and search.
51 |
52 | This package builds with Swift Package Manager and is part of the [Perfect](https://github.com/PerfectlySoft/Perfect) project.
53 |
54 | Ensure you have installed Xcode 8.0 or later.
55 |
56 | ## Setup - Xcode 8
57 |
58 |
59 | * Check out or download the project;
60 | * In terminal, navigate to the directory and execute
61 |
62 | ```
63 | swift package generate-xcodeproj
64 | ```
65 |
66 | * Open `PerfectURLShortener.xcodeproj`
67 |
68 | Due to the complexity of running static file serving from Xcode, we suggest running this project from Terminal.
69 |
70 | Optionally, to run from within Xcode, edit the Scheme, Under "Options" for "run", check "Use custom working directory" and choose the project's working directory. After doing this, the project can be run from within Xcode.
71 |
72 | NOTE: Due to a late-breaking bug in Xcode 8, if you wish to run directly within Xcode, we recommend [installing swiftenv](https://swiftenv.fuller.li/en/latest/) and installing the Swift 3.0.1 preview toolchain.
73 |
74 | ```
75 | # after installing swiftenv from https://swiftenv.fuller.li/en/latest/
76 | swiftenv install https://swift.org/builds/swift-3.0.1-preview-1/xcode/swift-3.0.1-PREVIEW-1/swift-3.0.1-PREVIEW-1-osx.pkg
77 | ```
78 |
79 |
80 | ## Setup - Terminal
81 |
82 | * Check out or download the project;
83 | * In terminal, navigate to the directory
84 | * Execute `swift build`
85 | * Once the project has compiled, execute `./.build/debug/PerfectURLShortener`
86 |
87 | ```
88 | [INFO] Starting HTTP server on 0.0.0.0:8181 with document root ./webroot
89 | ```
90 |
91 | ## Routes
92 |
93 | * [http://localhost:8181](http://localhost:8181) - Form input and display of existing entries.
94 | * http://localhost:8181/to/{shortcut} - Will redirect to the allocated URL.
95 |
96 | ## Issues
97 |
98 | We are transitioning to using JIRA for all bugs and support related issues, therefore the GitHub issues has been disabled.
99 |
100 | If you find a mistake, bug, or any other helpful suggestion you'd like to make on the docs please head over to [http://jira.perfect.org:8080/servicedesk/customer/portal/1](http://jira.perfect.org:8080/servicedesk/customer/portal/1) and raise it.
101 |
102 | A comprehensive list of open issues can be found at [http://jira.perfect.org:8080/projects/ISS/issues](http://jira.perfect.org:8080/projects/ISS/issues)
103 |
104 |
105 | ## Further Information
106 | For more information on the Perfect project, please visit [perfect.org](http://perfect.org).
107 |
--------------------------------------------------------------------------------
/Sources/Database.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Database.swift
3 | // Perfect-URL-Shortener
4 | //
5 | // Created by Jonathan Guthrie on 2016-09-28.
6 | //
7 | //
8 |
9 | import PerfectLib
10 | import SQLite
11 | import PerfectHTTP
12 | import PerfectMustache
13 | import SwiftString
14 |
15 | // bandles database interactions
16 | class DB {
17 | // The path to the SQLite database file
18 | let dbPath = "./url-database"
19 |
20 | // Create the database, and table.
21 | func create() {
22 | do {
23 | let sqlite = try SQLite(dbPath)
24 | defer {
25 | sqlite.close() // This makes sure we close our connection.
26 | }
27 |
28 | try sqlite.execute(statement: "CREATE TABLE IF NOT EXISTS urls (id TEXT PRIMARY KEY NOT NULL, url TEXT NOT NULL, sanity TEXT NOT NULL)")
29 |
30 | } catch {
31 | //Handle Errors
32 | print(error)
33 | }
34 | }
35 |
36 |
37 |
38 | // The function that retrieves the list of blog posts
39 | func getList() -> [URLify] {
40 | var data = [URLify]()
41 |
42 | do {
43 | let sqlite = try SQLite(dbPath)
44 | defer {
45 | sqlite.close() // This makes sure we close our connection.
46 | }
47 |
48 | let demoStatement = "SELECT id,url,sanity FROM urls"
49 |
50 | try sqlite.forEachRow(statement: demoStatement, handleRow: {(statement: SQLiteStmt, i:Int) -> () in
51 | var this = URLify()
52 | this.id = String(statement.columnText(position: 0))
53 | this.url = String(statement.columnText(position: 1))
54 | this.sanity = String(statement.columnText(position: 2))
55 | data.append(this)
56 | })
57 |
58 | } catch {
59 | //Handle Errors
60 | print(error)
61 | }
62 | return data
63 |
64 | }
65 |
66 | // Getting the url
67 | func getURL(_ opt: String) -> String {
68 | var url = ""
69 |
70 | do {
71 | let sqlite = try SQLite(dbPath)
72 | defer {
73 | sqlite.close() // This makes sure we close our connection.
74 | }
75 |
76 | try sqlite.forEachRow(statement: "SELECT url FROM urls WHERE sanity = ? LIMIT 1", doBindings: {
77 |
78 | (statement: SQLiteStmt) -> () in
79 | try statement.bind(position: 1, opt)
80 |
81 | }, handleRow: {(statement: SQLiteStmt, i:Int) -> () in
82 | url = String(statement.columnText(position: 0))
83 | })
84 |
85 | } catch {
86 | //Handle Errors
87 | print(error)
88 | }
89 | return url
90 | }
91 |
92 |
93 | // Saving the url
94 | func saveURL(_ this: URLify) -> URLify {
95 | var data = URLify()
96 | do {
97 | let sqlite = try SQLite(dbPath)
98 | defer {
99 | sqlite.close() // This makes sure we close our connection.
100 | }
101 |
102 | let demoStatement = "INSERT INTO urls (id,url,sanity) VALUES(:1,:2,:3)"
103 |
104 | try sqlite.forEachRow(statement: demoStatement, doBindings: {
105 |
106 | (statement: SQLiteStmt) -> () in
107 |
108 | try statement.bind(position: 1, this.id)
109 | try statement.bind(position: 2, this.url)
110 | try statement.bind(position: 3, this.sanity)
111 |
112 | }, handleRow: {(statement: SQLiteStmt, i:Int) -> () in
113 | data.id = String(statement.columnText(position: 0))
114 | data.url = String(statement.columnText(position: 1))
115 | data.sanity = String(statement.columnText(position: 2))
116 | })
117 |
118 | } catch {
119 | //Handle Errors
120 | print(error)
121 | }
122 | return data
123 | }
124 | }
125 |
126 |
--------------------------------------------------------------------------------
/Sources/Handlers.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Handlers.swift
3 | // Perfect-URL-Shortener
4 | //
5 | // Created by Jonathan Guthrie on 2016-09-29.
6 | //
7 | //
8 |
9 | import PerfectLib
10 | import SQLite
11 | import PerfectHTTP
12 | import PerfectMustache
13 | import SwiftString
14 |
15 | /*
16 | These are the main Mustache handlers.
17 | They are called as the handlers from the routes in main.swift
18 | */
19 |
20 |
21 | struct IndexHandler: MustachePageHandler { // all template handlers must inherit from PageHandler
22 | // This is the function which all handlers must impliment.
23 | // It is called by the system to allow the handler to return the set of values which will be used when populating the template.
24 | // - parameter context: The MustacheWebEvaluationContext which provides access to the HTTPRequest containing all the information pertaining to the request
25 | // - parameter collector: The MustacheEvaluationOutputCollector which can be used to adjust the template output. For example a `defaultEncodingFunc` could be installed to change how outgoing values are encoded.
26 |
27 | func extendValuesForResponse(context contxt: MustacheWebEvaluationContext, collector: MustacheEvaluationOutputCollector) {
28 |
29 | var values = MustacheEvaluationContext.MapType()
30 | let dbHandler = DB()
31 | let data = dbHandler.getList()
32 | var ary = [Any]()
33 |
34 | for i in 0.. 0 && urlinput != "http://" {
72 | let newURL = URLify(urlinput)
73 | print("New URL: \(newURL.id), \(newURL.url), \(newURL.sanity)")
74 | let _ = dbHandler.saveURL(newURL)
75 | }
76 |
77 | let data = dbHandler.getList()
78 | var ary = [Any]()
79 |
80 | for i in 0.. String {
27 | let letters : String = "abcdefghijklmnopqrstuvwxyzZ0123456789"
28 | var randomString = ""
29 | for _ in 0..
2 |
3 |
4 |
5 | Perfect URL Shortener Demo
6 |
7 | {{> style}}
8 |
9 |
10 |
11 |
17 |
18 |
22 |
23 |
24 |
25 |
26 | ID |
27 | URL |
28 | New Link |
29 |
30 | {{#urls}}
31 |
32 | {{id}} |
33 | {{url}} |
34 | {{sanity}} |
35 |
36 | {{/urls}}
37 |
38 |
39 |
40 |
41 |
42 |