2020-11-11 22:49:55 +00:00
|
|
|
import Vapor
|
|
|
|
import Foundation
|
2020-11-15 09:34:52 +00:00
|
|
|
import FoundationNetworking
|
2020-11-12 21:23:25 +00:00
|
|
|
import Crypto
|
2020-11-11 22:49:55 +00:00
|
|
|
import Leaf
|
|
|
|
|
|
|
|
func routes(_ app: Application) throws {
|
|
|
|
// Render home page when root domain is loaded
|
|
|
|
app.get { req -> EventLoopFuture<View> in
|
2020-11-15 19:51:49 +00:00
|
|
|
// Get data from config file at /Resources/config.json
|
2020-11-11 22:49:55 +00:00
|
|
|
let fileData = try String(contentsOfFile: "\(app.directory.resourcesDirectory)/config.json").data(using: .utf8)
|
2020-11-15 19:51:49 +00:00
|
|
|
// Decode JSON file to Config struct
|
2020-11-11 22:49:55 +00:00
|
|
|
let config: Config = try! JSONDecoder().decode(Config.self, from: fileData!)
|
2020-11-15 19:51:49 +00:00
|
|
|
// Check if user is logged in
|
2020-11-12 21:23:25 +00:00
|
|
|
let loginStatus = req.session.data["loggedIn"] ?? "false"
|
2020-11-15 19:51:49 +00:00
|
|
|
// Change loginStatus to a boolean
|
2020-11-12 21:23:25 +00:00
|
|
|
let loginBool = loginStatus == "true" ? true : false
|
2020-11-15 19:51:49 +00:00
|
|
|
// Render home.leaf with config and login status as context
|
2020-11-12 21:23:25 +00:00
|
|
|
return req.view.render("home", LContext(config: config, loggedIn: loginBool))
|
2020-11-11 22:49:55 +00:00
|
|
|
}
|
|
|
|
|
2020-11-15 19:51:49 +00:00
|
|
|
// Return website status when /status/:url is loaded
|
2020-11-15 09:34:52 +00:00
|
|
|
app.get("status", ":url") { req -> [String: String] in
|
2020-11-15 19:51:49 +00:00
|
|
|
// Get URL from request parameters
|
2020-11-15 09:34:52 +00:00
|
|
|
let url = URL(string: "http://\(req.parameters.get("url")!)")
|
2020-11-15 19:51:49 +00:00
|
|
|
// Configure URLSession
|
2020-11-15 19:36:10 +00:00
|
|
|
let config = URLSessionConfiguration.default
|
2020-11-15 19:51:49 +00:00
|
|
|
// Set request timeouts to 5s, then assume offline
|
2020-11-15 19:36:10 +00:00
|
|
|
config.timeoutIntervalForRequest = 5
|
|
|
|
config.timeoutIntervalForResource = 5
|
2020-11-15 19:51:49 +00:00
|
|
|
// Declare statusDict for storing website status
|
2020-11-15 09:34:52 +00:00
|
|
|
var statusDict: [String:String] = [:]
|
2020-11-15 19:51:49 +00:00
|
|
|
// Declare URLSession request using URL from request parameters
|
2020-11-15 09:34:52 +00:00
|
|
|
var headReq = URLRequest(url: url!)
|
2020-11-15 19:51:49 +00:00
|
|
|
// Set HEAD request
|
2020-11-15 09:34:52 +00:00
|
|
|
headReq.httpMethod = "HEAD"
|
2020-11-15 19:51:49 +00:00
|
|
|
// Set request timeout to 5s, then assume offline
|
2020-11-15 19:36:10 +00:00
|
|
|
headReq.timeoutInterval = 5
|
2020-11-15 19:51:49 +00:00
|
|
|
// Create DispatchSemaphore to block thread until request is resolved and status stored
|
2020-11-15 09:34:52 +00:00
|
|
|
let semaphore = DispatchSemaphore(value: 0)
|
2020-11-15 19:51:49 +00:00
|
|
|
// Run Async URLSession dataTask
|
2020-11-15 09:34:52 +00:00
|
|
|
URLSession.shared.dataTask(with: headReq, completionHandler: { (_, response, error) in
|
2020-11-15 19:51:49 +00:00
|
|
|
// If the response is valid
|
2020-11-15 09:34:52 +00:00
|
|
|
if let httpURLResponse = response as? HTTPURLResponse {
|
2020-11-15 19:51:49 +00:00
|
|
|
// Store status code in statusDict
|
2020-11-15 09:34:52 +00:00
|
|
|
statusDict["code"] = String(httpURLResponse.statusCode)
|
2020-11-15 19:51:49 +00:00
|
|
|
// Store website status in statusDict
|
2020-11-15 09:43:27 +00:00
|
|
|
statusDict["down"] = "false"
|
2020-11-15 09:34:52 +00:00
|
|
|
}
|
2020-11-15 19:51:49 +00:00
|
|
|
// Signal DispatchSemaphore to stop blocking
|
2020-11-15 09:34:52 +00:00
|
|
|
semaphore.signal()
|
|
|
|
}).resume()
|
2020-11-15 19:51:49 +00:00
|
|
|
// Wait for semaphore signal
|
2020-11-15 09:34:52 +00:00
|
|
|
semaphore.wait()
|
2020-11-15 19:51:49 +00:00
|
|
|
// If statusDict is empty, return code 0, down true. Otherwise, return statusdict
|
2020-11-15 09:34:52 +00:00
|
|
|
return statusDict.count > 0 ? statusDict : ["code": "0", "down": "true"]
|
2020-11-11 22:49:55 +00:00
|
|
|
}
|
2020-11-12 21:23:25 +00:00
|
|
|
|
2020-11-15 19:51:49 +00:00
|
|
|
// Render login page on GET request to /login
|
2020-11-12 21:23:25 +00:00
|
|
|
app.get("login") { req -> EventLoopFuture<View> in
|
2020-11-15 19:51:49 +00:00
|
|
|
// Get data from config file at /Resources/config.json
|
2020-11-12 21:23:25 +00:00
|
|
|
let fileData = try String(contentsOfFile: "\(app.directory.resourcesDirectory)/config.json").data(using: .utf8)
|
2020-11-15 19:51:49 +00:00
|
|
|
// Decode JSON file to Config struct
|
2020-11-12 21:23:25 +00:00
|
|
|
let config: Config = try! JSONDecoder().decode(Config.self, from: fileData!)
|
2020-11-15 19:51:49 +00:00
|
|
|
// Render home.leaf with config as context
|
2020-11-12 21:23:25 +00:00
|
|
|
return req.view.render("login", LContext(config: config, loggedIn: false))
|
|
|
|
}
|
|
|
|
|
2020-11-15 19:51:49 +00:00
|
|
|
// Verify credentials and log in on POST request to /login
|
2020-11-12 21:23:25 +00:00
|
|
|
app.post("login") { req -> Response in
|
2020-11-15 19:51:49 +00:00
|
|
|
// Decode POST request data into Login struct
|
2020-11-12 21:23:25 +00:00
|
|
|
let data = try req.content.decode(Login.self)
|
2020-11-15 19:51:49 +00:00
|
|
|
// Get data from config file at /Resources/config.json
|
2020-11-12 21:23:25 +00:00
|
|
|
let fileData = try String(contentsOfFile: "\(app.directory.resourcesDirectory)/config.json").data(using: .utf8)
|
2020-11-15 19:51:49 +00:00
|
|
|
// Decode JSON file to Config struct
|
2020-11-12 21:23:25 +00:00
|
|
|
let config: Config = try! JSONDecoder().decode(Config.self, from: fileData!)
|
2020-11-15 19:51:49 +00:00
|
|
|
// Get password from POST request data
|
2020-11-12 21:23:25 +00:00
|
|
|
let loginPassData = data.password?.data(using: .utf8)
|
2020-11-15 19:51:49 +00:00
|
|
|
// Hash password in POST data using SHA256.hash() from SwiftCrypto
|
2020-11-12 21:23:25 +00:00
|
|
|
let loginPassHash = SHA256.hash(data: loginPassData ?? "".data(using: .utf8)!)
|
2020-11-15 19:51:49 +00:00
|
|
|
// Convert hash to string
|
2020-11-12 21:23:25 +00:00
|
|
|
let stringHash = loginPassHash.map { String(format: "%02hhx", $0) }.joined()
|
2020-11-15 19:51:49 +00:00
|
|
|
// If hash in config matches provided hash
|
2020-11-12 21:23:25 +00:00
|
|
|
if stringHash == config.passwordHash {
|
2020-11-15 19:51:49 +00:00
|
|
|
// Set logged in to true in session
|
2020-11-12 21:23:25 +00:00
|
|
|
req.session.data["loggedIn"] = "true"
|
2020-11-15 19:51:49 +00:00
|
|
|
// Redirect back to /
|
2020-11-12 21:23:25 +00:00
|
|
|
return try req.redirect(to: "/")
|
|
|
|
} else {
|
2020-11-15 19:51:49 +00:00
|
|
|
// If hashes do not match, return unauthorized error
|
2020-11-12 21:23:25 +00:00
|
|
|
throw Abort(.unauthorized)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-15 19:51:49 +00:00
|
|
|
// Destroy session on GET request to logout
|
2020-11-12 21:23:25 +00:00
|
|
|
app.get("logout") { req -> Response in
|
2020-11-15 19:51:49 +00:00
|
|
|
// Destroy session
|
2020-11-12 21:23:25 +00:00
|
|
|
req.session.destroy()
|
2020-11-15 19:51:49 +00:00
|
|
|
// Redirect back to /
|
2020-11-12 21:23:25 +00:00
|
|
|
return try req.redirect(to: "/")
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-11 22:49:55 +00:00
|
|
|
}
|