Add login functionality
This commit is contained in:
parent
7e8a6d6590
commit
43bdf9a746
1
.gitignore
vendored
1
.gitignore
vendored
@ -6,3 +6,4 @@ xcuserdata
|
||||
DerivedData/
|
||||
.DS_Store
|
||||
file:*
|
||||
Resources/config.json
|
@ -78,8 +78,8 @@
|
||||
"repositoryURL": "https://github.com/apple/swift-crypto.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "9b9d1868601a199334da5d14f4ab2d37d4f8d0c5",
|
||||
"version": "1.0.2"
|
||||
"revision": "9680b7251cd2be22caaed8f1468bd9e8915a62fb",
|
||||
"version": "1.1.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -13,7 +13,8 @@ let package = Package(
|
||||
.package(url: "https://github.com/vapor/leaf", .exact("4.0.0-tau.1")),
|
||||
.package(url: "https://github.com/vapor/leaf-kit", .exact("1.0.0-tau.1.1")),
|
||||
// Leaf Error Middleware for custom error pages
|
||||
.package(name: "LeafErrorMiddleware", url: "https://github.com/brokenhandsio/leaf-error-middleware.git", from: "2.0.0-beta")
|
||||
.package(name: "LeafErrorMiddleware", url: "https://github.com/brokenhandsio/leaf-error-middleware.git", from: "2.0.0-beta"),
|
||||
.package(url: "https://github.com/apple/swift-crypto.git", from: "1.1.2")
|
||||
],
|
||||
targets: [
|
||||
.target(
|
||||
@ -21,6 +22,7 @@ let package = Package(
|
||||
dependencies: [
|
||||
.product(name: "Vapor", package: "vapor"),
|
||||
.product(name: "Leaf", package: "leaf"),
|
||||
.product(name: "Crypto", package: "swift-crypto"),
|
||||
"LeafErrorMiddleware"
|
||||
],
|
||||
swiftSettings: [
|
||||
|
@ -14,13 +14,15 @@
|
||||
<div class="container">
|
||||
<div class="d-flex">
|
||||
<a class="header-brand" href="/">#(config.title)</a>
|
||||
#if(config.showSourceBtn):
|
||||
<div class="d-flex order-lg-2 ml-auto">
|
||||
<div class="nav-item d-none d-md-flex">
|
||||
<a href="https://gitea.arsenm.dev/Arsen6331/statusboard" class="btn btn-sm btn-outline-primary" target="_blank">Source code</a>
|
||||
</div>
|
||||
<div class="d-flex order-lg-2 ml-auto">
|
||||
<div class="nav-item d-none d-md-flex">
|
||||
#if(loggedIn):
|
||||
<a href="/logout" class="btn btn-sm btn-outline-primary" target="_self">Log Out</a>
|
||||
#else:
|
||||
<a href="/login" class="btn btn-sm btn-outline-primary" target="_self">Log In</a>
|
||||
#endif
|
||||
</div>
|
||||
#endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
43
Resources/Views/card.leaf
Normal file
43
Resources/Views/card.leaf
Normal file
@ -0,0 +1,43 @@
|
||||
<div class="col">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">#(service["name"])</h3>
|
||||
<div class="card-options">
|
||||
<span class="tag #if(service["url"]): btn-loading #endif">
|
||||
Status
|
||||
<span id="#(service["name"])Status" class="tag-addon">Unavailable</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
#(service["description"])
|
||||
</div>
|
||||
#if(service["url"]):
|
||||
<div class="card-footer">
|
||||
URL: <a href="#(service["url"])">#(service["url"])</a>
|
||||
</div>
|
||||
#endif
|
||||
</div>
|
||||
#if(service["url"]):
|
||||
<script>
|
||||
fullURL = '#(service["url"])'
|
||||
var url = fullURL.replace("https://", "")
|
||||
url = url.replace("http://", "")
|
||||
var request = new XMLHttpRequest()
|
||||
request.open('GET', "/status/" + url, true)
|
||||
request.onload = function () {
|
||||
var data = JSON.parse(this.response)
|
||||
if (data.down === "true" || parseInt(data.code) > 500 && parseInt(data.code) < 600 ) {
|
||||
document.getElementById('#(service["name"])Status').classList.add("tag-danger")
|
||||
document.getElementById('#(service["name"])Status').parentElement.classList.remove("btn-loading")
|
||||
document.getElementById('#(service["name"])Status').innerHTML = "Offline"
|
||||
} else {
|
||||
document.getElementById('#(service["name"])Status').classList.add("tag-success")
|
||||
document.getElementById('#(service["name"])Status').parentElement.classList.remove("btn-loading")
|
||||
document.getElementById('#(service["name"])Status').innerHTML = "Online"
|
||||
}
|
||||
}
|
||||
request.send()
|
||||
</script>
|
||||
#endif
|
||||
</div>
|
@ -7,49 +7,13 @@
|
||||
<h6>#(node)</h6>
|
||||
<div class="row row-cards row-deck">
|
||||
#for(service in services):
|
||||
<div class="col">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">#(service["name"])</h3>
|
||||
<div class="card-options">
|
||||
<span class="tag #if(service["url"]): btn-loading #endif">
|
||||
Status
|
||||
<span id="#(service["name"])Status" class="tag-addon">Unavailable</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
#(service["description"])
|
||||
</div>
|
||||
#if(service["url"]):
|
||||
<div class="card-footer">
|
||||
URL: <a href="#(service["url"])">#(service["url"])</a>
|
||||
</div>
|
||||
#endif
|
||||
</div>
|
||||
#if(service["url"]):
|
||||
<script>
|
||||
fullURL = '#(service["url"])'
|
||||
var url = fullURL.replace("https://", "")
|
||||
url = url.replace("http://", "")
|
||||
var request = new XMLHttpRequest()
|
||||
request.open('GET', "/status/" + url, true)
|
||||
request.onload = function () {
|
||||
var data = JSON.parse(this.response)
|
||||
if (data.down === "true" || parseInt(data.code) > 500 && parseInt(data.code) < 600 ) {
|
||||
document.getElementById('#(service["name"])Status').classList.add("tag-danger")
|
||||
document.getElementById('#(service["name"])Status').parentElement.classList.remove("btn-loading")
|
||||
document.getElementById('#(service["name"])Status').innerHTML = "Offline"
|
||||
} else {
|
||||
document.getElementById('#(service["name"])Status').classList.add("tag-success")
|
||||
document.getElementById('#(service["name"])Status').parentElement.classList.remove("btn-loading")
|
||||
document.getElementById('#(service["name"])Status').innerHTML = "Online"
|
||||
}
|
||||
}
|
||||
request.send()
|
||||
</script>
|
||||
#if(!service["private"] == "true"):
|
||||
#inline("card")
|
||||
#else:
|
||||
#if(loggedIn):
|
||||
#inline("card")
|
||||
#endif
|
||||
</div>
|
||||
#endif
|
||||
#endfor
|
||||
</div>
|
||||
#endfor
|
||||
|
30
Resources/Views/login.leaf
Normal file
30
Resources/Views/login.leaf
Normal file
@ -0,0 +1,30 @@
|
||||
#define(body):
|
||||
<div class="page">
|
||||
<div class="page-single">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col col-login mx-auto">
|
||||
<div class="text-center mb-6">
|
||||
<img src="" class="h-6" alt="">
|
||||
</div>
|
||||
<form class="card" action="/login" method="post">
|
||||
<div class="card-body p-6">
|
||||
<div class="card-title">Login to #(config.title)</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">
|
||||
Password
|
||||
</label>
|
||||
<input type="password" class="form-control" id="passwordInput" name="password" value="password" placeholder="Password">
|
||||
</div>
|
||||
<div class="form-footer">
|
||||
<button type="submit" class="btn btn-primary btn-block">Sign in</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
#enddefine
|
||||
#inline("base")
|
@ -3,6 +3,6 @@ import Vapor
|
||||
|
||||
struct Config: Codable {
|
||||
let title: String
|
||||
let showSourceBtn: Bool
|
||||
let passwordHash: String
|
||||
let services: [String:[[String:String]]]
|
||||
}
|
@ -8,6 +8,9 @@ public func configure(_ app: Application) throws {
|
||||
// Serve files from /Public
|
||||
app.middleware.use(FileMiddleware(publicDirectory: app.directory.publicDirectory))
|
||||
|
||||
app.middleware.use(app.sessions.middleware)
|
||||
app.sessions.configuration.cookieName = "statusboard_session"
|
||||
|
||||
// Configure Leaf
|
||||
LeafOption.caching = app.environment.isRelease ? .default : .bypass
|
||||
LeafRenderer.Option.timeout = 200.0
|
||||
|
7
Sources/App/context.swift
Normal file
7
Sources/App/context.swift
Normal file
@ -0,0 +1,7 @@
|
||||
import Foundation
|
||||
import Vapor
|
||||
|
||||
struct LContext: Codable {
|
||||
let config: Config
|
||||
let loggedIn: Bool
|
||||
}
|
6
Sources/App/login.swift
Normal file
6
Sources/App/login.swift
Normal file
@ -0,0 +1,6 @@
|
||||
import Foundation
|
||||
import Vapor
|
||||
|
||||
struct Login: Codable {
|
||||
let password: String?
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
import Vapor
|
||||
import Foundation
|
||||
import Crypto
|
||||
import Leaf
|
||||
|
||||
func routes(_ app: Application) throws {
|
||||
@ -7,7 +8,9 @@ func routes(_ app: Application) throws {
|
||||
app.get { req -> EventLoopFuture<View> in
|
||||
let fileData = try String(contentsOfFile: "\(app.directory.resourcesDirectory)/config.json").data(using: .utf8)
|
||||
let config: Config = try! JSONDecoder().decode(Config.self, from: fileData!)
|
||||
return req.view.render("home", ["config": config])
|
||||
let loginStatus = req.session.data["loggedIn"] ?? "false"
|
||||
let loginBool = loginStatus == "true" ? true : false
|
||||
return req.view.render("home", LContext(config: config, loggedIn: loginBool))
|
||||
}
|
||||
|
||||
app.get("status", ":url") { req -> EventLoopFuture<[String: String]> in
|
||||
@ -18,4 +21,34 @@ func routes(_ app: Application) throws {
|
||||
["down": String(json.isitdown), "code": String(json.response_code)]
|
||||
}
|
||||
}
|
||||
|
||||
app.get("login") { req -> EventLoopFuture<View> in
|
||||
let fileData = try String(contentsOfFile: "\(app.directory.resourcesDirectory)/config.json").data(using: .utf8)
|
||||
let config: Config = try! JSONDecoder().decode(Config.self, from: fileData!)
|
||||
return req.view.render("login", LContext(config: config, loggedIn: false))
|
||||
}
|
||||
|
||||
app.post("login") { req -> Response in
|
||||
let data = try req.content.decode(Login.self)
|
||||
let fileData = try String(contentsOfFile: "\(app.directory.resourcesDirectory)/config.json").data(using: .utf8)
|
||||
let config: Config = try! JSONDecoder().decode(Config.self, from: fileData!)
|
||||
let loginPassData = data.password?.data(using: .utf8)
|
||||
let loginPassHash = SHA256.hash(data: loginPassData ?? "".data(using: .utf8)!)
|
||||
let stringHash = loginPassHash.map { String(format: "%02hhx", $0) }.joined()
|
||||
print("Recv: \(stringHash)")
|
||||
print("Conf: \(config.passwordHash)")
|
||||
if stringHash == config.passwordHash {
|
||||
req.session.data["loggedIn"] = "true"
|
||||
return try req.redirect(to: "/")
|
||||
} else {
|
||||
throw Abort(.unauthorized)
|
||||
}
|
||||
}
|
||||
|
||||
app.get("logout") { req -> Response in
|
||||
req.session.destroy()
|
||||
return try req.redirect(to: "/")
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user