forked from PeachCloud/peach-workspace
Compare commits
7 Commits
main
...
lib_error_
Author | SHA1 | Date | |
---|---|---|---|
406206bab3 | |||
c72c57c345 | |||
bf9dd7f567 | |||
e6a6fcdc89 | |||
b2f7747357 | |||
f17ae95b21 | |||
91180ed1fe |
218
Cargo.lock
generated
218
Cargo.lock
generated
@ -204,6 +204,15 @@ dependencies = [
|
|||||||
"safemem",
|
"safemem",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64"
|
||||||
|
version = "0.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64"
|
name = "base64"
|
||||||
version = "0.13.0"
|
version = "0.13.0"
|
||||||
@ -464,6 +473,22 @@ dependencies = [
|
|||||||
"version_check 0.9.3",
|
"version_check 0.9.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "core-foundation"
|
||||||
|
version = "0.9.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6888e10551bb93e424d8df1d07f1a8b4fceb0001a3a4b048bfc47554946f47b3"
|
||||||
|
dependencies = [
|
||||||
|
"core-foundation-sys",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "core-foundation-sys"
|
||||||
|
version = "0.8.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cpufeatures"
|
name = "cpufeatures"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
@ -1314,6 +1339,25 @@ version = "2.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hyper"
|
||||||
|
version = "0.10.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0a0652d9a2609a968c14be1a9ea00bf4b1d64e2e1f53a1b51b6fff3a6e829273"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.9.3",
|
||||||
|
"httparse",
|
||||||
|
"language-tags",
|
||||||
|
"log 0.3.9",
|
||||||
|
"mime 0.2.6",
|
||||||
|
"num_cpus",
|
||||||
|
"time 0.1.44",
|
||||||
|
"traitobject",
|
||||||
|
"typeable",
|
||||||
|
"unicase 1.4.2",
|
||||||
|
"url",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper"
|
name = "hyper"
|
||||||
version = "0.11.27"
|
version = "0.11.27"
|
||||||
@ -1328,7 +1372,7 @@ dependencies = [
|
|||||||
"iovec",
|
"iovec",
|
||||||
"language-tags",
|
"language-tags",
|
||||||
"log 0.4.14",
|
"log 0.4.14",
|
||||||
"mime",
|
"mime 0.3.16",
|
||||||
"net2",
|
"net2",
|
||||||
"percent-encoding 1.0.1",
|
"percent-encoding 1.0.1",
|
||||||
"relay",
|
"relay",
|
||||||
@ -1337,7 +1381,7 @@ dependencies = [
|
|||||||
"tokio-io",
|
"tokio-io",
|
||||||
"tokio-proto",
|
"tokio-proto",
|
||||||
"tokio-service",
|
"tokio-service",
|
||||||
"unicase",
|
"unicase 2.6.0",
|
||||||
"want 0.0.4",
|
"want 0.0.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1630,7 +1674,7 @@ dependencies = [
|
|||||||
"jsonrpc-server-utils 11.0.0",
|
"jsonrpc-server-utils 11.0.0",
|
||||||
"log 0.4.14",
|
"log 0.4.14",
|
||||||
"net2",
|
"net2",
|
||||||
"unicase",
|
"unicase 2.6.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1646,7 +1690,7 @@ dependencies = [
|
|||||||
"log 0.4.14",
|
"log 0.4.14",
|
||||||
"net2",
|
"net2",
|
||||||
"parking_lot 0.11.2",
|
"parking_lot 0.11.2",
|
||||||
"unicase",
|
"unicase 2.6.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1690,7 +1734,7 @@ dependencies = [
|
|||||||
"num_cpus",
|
"num_cpus",
|
||||||
"tokio 0.1.22",
|
"tokio 0.1.22",
|
||||||
"tokio-codec",
|
"tokio-codec",
|
||||||
"unicase",
|
"unicase 2.6.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1708,7 +1752,7 @@ dependencies = [
|
|||||||
"tokio 1.13.0",
|
"tokio 1.13.0",
|
||||||
"tokio-stream",
|
"tokio-stream",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
"unicase",
|
"unicase 2.6.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1928,6 +1972,15 @@ dependencies = [
|
|||||||
"autocfg 1.0.1",
|
"autocfg 1.0.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mime"
|
||||||
|
version = "0.2.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0"
|
||||||
|
dependencies = [
|
||||||
|
"log 0.3.9",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mime"
|
name = "mime"
|
||||||
version = "0.3.16"
|
version = "0.3.16"
|
||||||
@ -2066,7 +2119,7 @@ dependencies = [
|
|||||||
"http 0.2.5",
|
"http 0.2.5",
|
||||||
"httparse",
|
"httparse",
|
||||||
"log 0.4.14",
|
"log 0.4.14",
|
||||||
"mime",
|
"mime 0.3.16",
|
||||||
"spin",
|
"spin",
|
||||||
"tokio 1.13.0",
|
"tokio 1.13.0",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
@ -2074,6 +2127,24 @@ dependencies = [
|
|||||||
"version_check 0.9.3",
|
"version_check 0.9.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "native-tls"
|
||||||
|
version = "0.2.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "48ba9f7719b5a0f42f338907614285fb5fd70e53858141f69898a1fb7203b24d"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
|
"libc",
|
||||||
|
"log 0.4.14",
|
||||||
|
"openssl",
|
||||||
|
"openssl-probe",
|
||||||
|
"openssl-sys",
|
||||||
|
"schannel",
|
||||||
|
"security-framework",
|
||||||
|
"security-framework-sys",
|
||||||
|
"tempfile",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nb"
|
name = "nb"
|
||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
@ -2338,6 +2409,12 @@ dependencies = [
|
|||||||
"openssl-sys",
|
"openssl-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "openssl-probe"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "openssl-src"
|
name = "openssl-src"
|
||||||
version = "300.0.2+3.0.0"
|
version = "300.0.2+3.0.0"
|
||||||
@ -2494,7 +2571,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "peach-lib"
|
name = "peach-lib"
|
||||||
version = "1.2.15"
|
version = "1.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"env_logger 0.6.2",
|
"env_logger 0.6.2",
|
||||||
@ -2507,10 +2584,8 @@ dependencies = [
|
|||||||
"regex",
|
"regex",
|
||||||
"rust-crypto",
|
"rust-crypto",
|
||||||
"serde 1.0.130",
|
"serde 1.0.130",
|
||||||
"serde_derive",
|
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_yaml",
|
"serde_yaml",
|
||||||
"snafu 0.6.10",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2563,17 +2638,19 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "peach-oled"
|
name = "peach-oled"
|
||||||
version = "0.1.4"
|
version = "0.1.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"embedded-graphics",
|
"embedded-graphics",
|
||||||
"env_logger 0.9.0",
|
"env_logger 0.6.2",
|
||||||
"jsonrpc-core 18.0.0",
|
"jsonrpc-core 11.0.0",
|
||||||
"jsonrpc-http-server 18.0.0",
|
"jsonrpc-http-server 11.0.0",
|
||||||
"jsonrpc-test 18.0.0",
|
"jsonrpc-test 11.0.0",
|
||||||
"linux-embedded-hal",
|
"linux-embedded-hal",
|
||||||
"log 0.4.14",
|
"log 0.4.14",
|
||||||
"nix 0.11.1",
|
"nix 0.11.1",
|
||||||
"serde 1.0.130",
|
"serde 1.0.130",
|
||||||
|
"serde_json",
|
||||||
|
"snafu 0.4.4",
|
||||||
"ssd1306",
|
"ssd1306",
|
||||||
"tinybmp",
|
"tinybmp",
|
||||||
]
|
]
|
||||||
@ -2614,7 +2691,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "peach-web"
|
name = "peach-web"
|
||||||
version = "0.4.12"
|
version = "0.4.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"env_logger 0.8.4",
|
"env_logger 0.8.4",
|
||||||
"log 0.4.14",
|
"log 0.4.14",
|
||||||
@ -2629,6 +2706,7 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"snafu 0.6.10",
|
"snafu 0.6.10",
|
||||||
"tera",
|
"tera",
|
||||||
|
"websocket",
|
||||||
"xdg",
|
"xdg",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -3286,7 +3364,7 @@ dependencies = [
|
|||||||
"indexmap",
|
"indexmap",
|
||||||
"log 0.4.14",
|
"log 0.4.14",
|
||||||
"memchr",
|
"memchr",
|
||||||
"mime",
|
"mime 0.3.16",
|
||||||
"parking_lot 0.11.2",
|
"parking_lot 0.11.2",
|
||||||
"pear",
|
"pear",
|
||||||
"percent-encoding 2.1.0",
|
"percent-encoding 2.1.0",
|
||||||
@ -3389,6 +3467,16 @@ dependencies = [
|
|||||||
"winapi-util",
|
"winapi-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "schannel"
|
||||||
|
version = "0.1.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
|
"winapi 0.3.9",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scoped-tls"
|
name = "scoped-tls"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
@ -3413,6 +3501,29 @@ version = "1.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "security-framework"
|
||||||
|
version = "2.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "525bc1abfda2e1998d152c45cf13e696f76d0a4972310b22fac1658b05df7c87"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"core-foundation",
|
||||||
|
"core-foundation-sys",
|
||||||
|
"libc",
|
||||||
|
"security-framework-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "security-framework-sys"
|
||||||
|
version = "2.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a9dd14d83160b528b7bfd66439110573efcfbe281b17fc2ca9f39f550d619c7e"
|
||||||
|
dependencies = [
|
||||||
|
"core-foundation-sys",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "semver"
|
name = "semver"
|
||||||
version = "0.1.20"
|
version = "0.1.20"
|
||||||
@ -4320,6 +4431,17 @@ dependencies = [
|
|||||||
"tokio-executor",
|
"tokio-executor",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-tls"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "354b8cd83825b3c20217a9dc174d6a0c67441a2fae5c41bcb1ea6679f6ae0f7c"
|
||||||
|
dependencies = [
|
||||||
|
"futures 0.1.31",
|
||||||
|
"native-tls",
|
||||||
|
"tokio-io",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-udp"
|
name = "tokio-udp"
|
||||||
version = "0.1.6"
|
version = "0.1.6"
|
||||||
@ -4458,6 +4580,12 @@ dependencies = [
|
|||||||
"tracing-serde",
|
"tracing-serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "traitobject"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "try-lock"
|
name = "try-lock"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@ -4480,6 +4608,12 @@ dependencies = [
|
|||||||
"unchecked-index",
|
"unchecked-index",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typeable"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typenum"
|
name = "typenum"
|
||||||
version = "1.14.0"
|
version = "1.14.0"
|
||||||
@ -4567,6 +4701,15 @@ dependencies = [
|
|||||||
"unic-common",
|
"unic-common",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicase"
|
||||||
|
version = "1.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33"
|
||||||
|
dependencies = [
|
||||||
|
"version_check 0.1.5",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicase"
|
name = "unicase"
|
||||||
version = "2.6.0"
|
version = "2.6.0"
|
||||||
@ -4775,6 +4918,47 @@ version = "0.2.78"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc"
|
checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "websocket"
|
||||||
|
version = "0.26.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "723abe6b75286edc51d8ecabb38a2353f62a9e9b0588998b59111474f1dcd637"
|
||||||
|
dependencies = [
|
||||||
|
"bytes 0.4.12",
|
||||||
|
"futures 0.1.31",
|
||||||
|
"hyper 0.10.16",
|
||||||
|
"native-tls",
|
||||||
|
"rand 0.6.5",
|
||||||
|
"tokio-codec",
|
||||||
|
"tokio-io",
|
||||||
|
"tokio-reactor",
|
||||||
|
"tokio-tcp",
|
||||||
|
"tokio-tls",
|
||||||
|
"unicase 1.4.2",
|
||||||
|
"url",
|
||||||
|
"websocket-base",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "websocket-base"
|
||||||
|
version = "0.26.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "403f3fd505ff930da84156389639932955fb09705b3dccd1a3d60c8e7ff62776"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.10.1",
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"byteorder",
|
||||||
|
"bytes 0.4.12",
|
||||||
|
"futures 0.1.31",
|
||||||
|
"native-tls",
|
||||||
|
"rand 0.6.5",
|
||||||
|
"sha-1",
|
||||||
|
"tokio-codec",
|
||||||
|
"tokio-io",
|
||||||
|
"tokio-tcp",
|
||||||
|
"tokio-tls",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.2.8"
|
version = "0.2.8"
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "peach-lib"
|
name = "peach-lib"
|
||||||
version = "1.2.15"
|
version = "1.3.0"
|
||||||
authors = ["Andrew Reid <gnomad@cryptolab.net>"]
|
authors = ["Andrew Reid <glyph@mycelial.technology>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
jsonrpc-client-core = "0.5"
|
jsonrpc-client-core = "0.5"
|
||||||
@ -13,11 +11,9 @@ jsonrpc-client-http = "0.5"
|
|||||||
jsonrpc-core = "8.0.1"
|
jsonrpc-core = "8.0.1"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
rust-crypto = "0.2.36"
|
rust-crypto = "0.2.36"
|
||||||
serde_derive = "1.0"
|
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
serde_yaml = "0.8"
|
serde_yaml = "0.8"
|
||||||
env_logger = "0.6"
|
env_logger = "0.6"
|
||||||
snafu = "0.6"
|
|
||||||
regex = "1"
|
regex = "1"
|
||||||
chrono = "0.4.19"
|
chrono = "0.4.19"
|
||||||
rand="0.8.4"
|
rand="0.8.4"
|
||||||
|
@ -4,12 +4,12 @@
|
|||||||
//!
|
//!
|
||||||
//! The configuration file is located at: "/var/lib/peachcloud/config.yml"
|
//! The configuration file is located at: "/var/lib/peachcloud/config.yml"
|
||||||
|
|
||||||
use fslock::{LockFile};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
|
||||||
|
use fslock::LockFile;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::error::PeachError;
|
use crate::error::PeachError;
|
||||||
use crate::error::*;
|
|
||||||
|
|
||||||
// main configuration file
|
// main configuration file
|
||||||
pub const YAML_PATH: &str = "/var/lib/peachcloud/config.yml";
|
pub const YAML_PATH: &str = "/var/lib/peachcloud/config.yml";
|
||||||
@ -48,8 +48,9 @@ fn save_peach_config(peach_config: PeachConfig) -> Result<PeachConfig, PeachErro
|
|||||||
|
|
||||||
let yaml_str = serde_yaml::to_string(&peach_config)?;
|
let yaml_str = serde_yaml::to_string(&peach_config)?;
|
||||||
|
|
||||||
fs::write(YAML_PATH, yaml_str).context(WriteConfigError {
|
fs::write(YAML_PATH, yaml_str).map_err(|source| PeachError::Write {
|
||||||
file: YAML_PATH.to_string(),
|
source,
|
||||||
|
path: YAML_PATH.to_string(),
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// unlock file lock
|
// unlock file lock
|
||||||
@ -79,8 +80,9 @@ pub fn load_peach_config() -> Result<PeachConfig, PeachError> {
|
|||||||
}
|
}
|
||||||
// otherwise we load peach config from disk
|
// otherwise we load peach config from disk
|
||||||
else {
|
else {
|
||||||
let contents = fs::read_to_string(YAML_PATH).context(ReadConfigError {
|
let contents = fs::read_to_string(YAML_PATH).map_err(|source| PeachError::Read {
|
||||||
file: YAML_PATH.to_string(),
|
source,
|
||||||
|
path: YAML_PATH.to_string(),
|
||||||
})?;
|
})?;
|
||||||
peach_config = serde_yaml::from_str(&contents)?;
|
peach_config = serde_yaml::from_str(&contents)?;
|
||||||
}
|
}
|
||||||
|
@ -9,24 +9,24 @@
|
|||||||
//!
|
//!
|
||||||
//! The domain for dyndns updates is stored in /var/lib/peachcloud/config.yml
|
//! The domain for dyndns updates is stored in /var/lib/peachcloud/config.yml
|
||||||
//! The tsig key for authenticating the updates is stored in /var/lib/peachcloud/peach-dyndns/tsig.key
|
//! The tsig key for authenticating the updates is stored in /var/lib/peachcloud/peach-dyndns/tsig.key
|
||||||
use crate::config_manager::{load_peach_config, set_peach_dyndns_config};
|
use std::{
|
||||||
use crate::error::PeachError;
|
fs,
|
||||||
use crate::error::{
|
fs::OpenOptions,
|
||||||
ChronoParseError, DecodeNsUpdateOutputError, DecodePublicIpError, GetPublicIpError,
|
io::Write,
|
||||||
NsCommandError, SaveDynDnsResultError, SaveTsigKeyError,
|
process::{Command, Stdio},
|
||||||
|
str::FromStr,
|
||||||
};
|
};
|
||||||
|
|
||||||
use chrono::prelude::*;
|
use chrono::prelude::*;
|
||||||
use jsonrpc_client_core::{expand_params, jsonrpc_client};
|
use jsonrpc_client_core::{expand_params, jsonrpc_client};
|
||||||
use jsonrpc_client_http::HttpTransport;
|
use jsonrpc_client_http::HttpTransport;
|
||||||
use log::{debug, info};
|
use log::{debug, info};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use snafu::ResultExt;
|
|
||||||
use std::fs;
|
use crate::{
|
||||||
use std::fs::OpenOptions;
|
config_manager::{load_peach_config, set_peach_dyndns_config},
|
||||||
use std::io::Write;
|
error::PeachError,
|
||||||
use std::process::{Command, Stdio};
|
};
|
||||||
use std::str::FromStr;
|
|
||||||
use std::str::ParseBoolError;
|
|
||||||
|
|
||||||
/// constants for dyndns configuration
|
/// constants for dyndns configuration
|
||||||
pub const PEACH_DYNDNS_URL: &str = "http://dynserver.dyn.peachcloud.org";
|
pub const PEACH_DYNDNS_URL: &str = "http://dynserver.dyn.peachcloud.org";
|
||||||
@ -37,20 +37,21 @@ pub const DYNDNS_LOG_PATH: &str = "/var/lib/peachcloud/peach-dyndns/latest_resul
|
|||||||
/// helper function which saves dyndns TSIG key returned by peach-dyndns-server to /var/lib/peachcloud/peach-dyndns/tsig.key
|
/// helper function which saves dyndns TSIG key returned by peach-dyndns-server to /var/lib/peachcloud/peach-dyndns/tsig.key
|
||||||
pub fn save_dyndns_key(key: &str) -> Result<(), PeachError> {
|
pub fn save_dyndns_key(key: &str) -> Result<(), PeachError> {
|
||||||
// create directory if it doesn't exist
|
// create directory if it doesn't exist
|
||||||
fs::create_dir_all(PEACH_DYNDNS_CONFIG_PATH).context(SaveTsigKeyError {
|
fs::create_dir_all(PEACH_DYNDNS_CONFIG_PATH)?;
|
||||||
path: PEACH_DYNDNS_CONFIG_PATH.to_string(),
|
//.context(SaveTsigKeyError {
|
||||||
})?;
|
//path: PEACH_DYNDNS_CONFIG_PATH.to_string(),
|
||||||
|
//})?;
|
||||||
// write key text
|
// write key text
|
||||||
let mut file = OpenOptions::new()
|
let mut file = OpenOptions::new()
|
||||||
.write(true)
|
.write(true)
|
||||||
.create(true)
|
.create(true)
|
||||||
.open(TSIG_KEY_PATH)
|
// TODO: consider adding context msg
|
||||||
.context(SaveTsigKeyError {
|
.open(TSIG_KEY_PATH)?;
|
||||||
path: TSIG_KEY_PATH.to_string(),
|
writeln!(file, "{}", key).map_err(|source| PeachError::Write {
|
||||||
})?;
|
source,
|
||||||
writeln!(file, "{}", key).context(SaveTsigKeyError {
|
|
||||||
path: TSIG_KEY_PATH.to_string(),
|
path: TSIG_KEY_PATH.to_string(),
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,23 +69,17 @@ pub fn register_domain(domain: &str) -> std::result::Result<String, PeachError>
|
|||||||
let mut client = PeachDynDnsClient::new(transport_handle);
|
let mut client = PeachDynDnsClient::new(transport_handle);
|
||||||
|
|
||||||
info!("Performing register_domain call to peach-dyndns-server");
|
info!("Performing register_domain call to peach-dyndns-server");
|
||||||
let res = client.register_domain(domain).call();
|
let key = client.register_domain(domain).call()?;
|
||||||
match res {
|
// save new TSIG key
|
||||||
Ok(key) => {
|
save_dyndns_key(&key)?;
|
||||||
// save new TSIG key
|
// save new configuration values
|
||||||
save_dyndns_key(&key)?;
|
let set_config_result = set_peach_dyndns_config(domain, PEACH_DYNDNS_URL, TSIG_KEY_PATH, true);
|
||||||
// save new configuration values
|
match set_config_result {
|
||||||
let set_config_result =
|
Ok(_) => {
|
||||||
set_peach_dyndns_config(domain, PEACH_DYNDNS_URL, TSIG_KEY_PATH, true);
|
let response = "success".to_string();
|
||||||
match set_config_result {
|
Ok(response)
|
||||||
Ok(_) => {
|
|
||||||
let response = "success".to_string();
|
|
||||||
Ok(response)
|
|
||||||
}
|
|
||||||
Err(err) => Err(err),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Err(err) => Err(PeachError::JsonRpcClientCore { source: err }),
|
Err(err) => Err(err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,29 +94,20 @@ pub fn is_domain_available(domain: &str) -> std::result::Result<bool, PeachError
|
|||||||
let mut client = PeachDynDnsClient::new(transport_handle);
|
let mut client = PeachDynDnsClient::new(transport_handle);
|
||||||
|
|
||||||
info!("Performing register_domain call to peach-dyndns-server");
|
info!("Performing register_domain call to peach-dyndns-server");
|
||||||
let res = client.is_domain_available(domain).call();
|
let domain_availability = client.is_domain_available(domain).call()?;
|
||||||
info!("res: {:?}", res);
|
info!("Domain availability: {:?}", domain_availability);
|
||||||
match res {
|
// convert availability status to a bool
|
||||||
Ok(result_str) => {
|
let available: bool = FromStr::from_str(&domain_availability)?;
|
||||||
let result: Result<bool, ParseBoolError> = FromStr::from_str(&result_str);
|
|
||||||
match result {
|
Ok(available)
|
||||||
Ok(result_bool) => Ok(result_bool),
|
|
||||||
Err(err) => Err(PeachError::PeachParseBoolError { source: err }),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(err) => Err(PeachError::JsonRpcClientCore { source: err }),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper function to get public ip address of PeachCloud device.
|
/// Helper function to get public ip address of PeachCloud device.
|
||||||
fn get_public_ip_address() -> Result<String, PeachError> {
|
fn get_public_ip_address() -> Result<String, PeachError> {
|
||||||
// TODO: consider other ways to get public IP address
|
// TODO: consider other ways to get public IP address
|
||||||
let output = Command::new("/usr/bin/curl")
|
let output = Command::new("/usr/bin/curl").arg("ifconfig.me").output()?;
|
||||||
.arg("ifconfig.me")
|
let command_output = String::from_utf8(output.stdout)?;
|
||||||
.output()
|
Ok(command_output)
|
||||||
.context(GetPublicIpError)?;
|
|
||||||
let command_output = std::str::from_utf8(&output.stdout).context(DecodePublicIpError)?;
|
|
||||||
Ok(command_output.to_string())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads dyndns configurations from config.yml
|
/// Reads dyndns configurations from config.yml
|
||||||
@ -146,13 +132,12 @@ pub fn dyndns_update_ip() -> Result<bool, PeachError> {
|
|||||||
Ok(false)
|
Ok(false)
|
||||||
} else {
|
} else {
|
||||||
// call nsupdate passing appropriate configs
|
// call nsupdate passing appropriate configs
|
||||||
let nsupdate_command = Command::new("/usr/bin/nsupdate")
|
let mut nsupdate_command = Command::new("/usr/bin/nsupdate")
|
||||||
.arg("-k")
|
.arg("-k")
|
||||||
.arg(peach_config.dyn_tsig_key_path)
|
.arg(&peach_config.dyn_tsig_key_path)
|
||||||
.arg("-v")
|
.arg("-v")
|
||||||
.stdin(Stdio::piped())
|
.stdin(Stdio::piped())
|
||||||
.spawn()
|
.spawn()?;
|
||||||
.context(NsCommandError)?;
|
|
||||||
// pass nsupdate commands via stdin
|
// pass nsupdate commands via stdin
|
||||||
let public_ip_address = get_public_ip_address()?;
|
let public_ip_address = get_public_ip_address()?;
|
||||||
info!("found public ip address: {}", public_ip_address);
|
info!("found public ip address: {}", public_ip_address);
|
||||||
@ -168,11 +153,15 @@ pub fn dyndns_update_ip() -> Result<bool, PeachError> {
|
|||||||
DOMAIN = peach_config.dyn_domain,
|
DOMAIN = peach_config.dyn_domain,
|
||||||
PUBLIC_IP_ADDRESS = public_ip_address,
|
PUBLIC_IP_ADDRESS = public_ip_address,
|
||||||
);
|
);
|
||||||
write!(nsupdate_command.stdin.as_ref().unwrap(), "{}", ns_commands).unwrap();
|
let mut nsupdate_stdin = nsupdate_command.stdin.take().ok_or(PeachError::NsUpdate {
|
||||||
let nsupdate_output = nsupdate_command
|
msg: "unable to capture stdin handle for `nsupdate` command".to_string(),
|
||||||
.wait_with_output()
|
})?;
|
||||||
.context(NsCommandError)?;
|
write!(nsupdate_stdin, "{}", ns_commands).map_err(|source| PeachError::Write {
|
||||||
info!("output: {:?}", nsupdate_output);
|
source,
|
||||||
|
path: peach_config.dyn_tsig_key_path.to_string(),
|
||||||
|
})?;
|
||||||
|
let nsupdate_output = nsupdate_command.wait_with_output()?;
|
||||||
|
info!("nsupdate output: {:?}", nsupdate_output);
|
||||||
// We only return a successful result if nsupdate was successful
|
// We only return a successful result if nsupdate was successful
|
||||||
if nsupdate_output.status.success() {
|
if nsupdate_output.status.success() {
|
||||||
info!("nsupdate succeeded, returning ok");
|
info!("nsupdate succeeded, returning ok");
|
||||||
@ -182,9 +171,8 @@ pub fn dyndns_update_ip() -> Result<bool, PeachError> {
|
|||||||
Ok(true)
|
Ok(true)
|
||||||
} else {
|
} else {
|
||||||
info!("nsupdate failed, returning error");
|
info!("nsupdate failed, returning error");
|
||||||
let err_msg =
|
let err_msg = String::from_utf8(nsupdate_output.stdout)?;
|
||||||
String::from_utf8(nsupdate_output.stdout).context(DecodeNsUpdateOutputError)?;
|
Err(PeachError::NsUpdate { msg: err_msg })
|
||||||
Err(PeachError::NsUpdateError { msg: err_msg })
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -195,9 +183,12 @@ pub fn log_successful_nsupdate() -> Result<bool, PeachError> {
|
|||||||
let mut file = OpenOptions::new()
|
let mut file = OpenOptions::new()
|
||||||
.write(true)
|
.write(true)
|
||||||
.create(true)
|
.create(true)
|
||||||
.open(DYNDNS_LOG_PATH)
|
// TODO: possibly add a context msg here ("failed to open dynamic dns success log")
|
||||||
.context(SaveDynDnsResultError)?;
|
.open(DYNDNS_LOG_PATH)?;
|
||||||
write!(file, "{}", now_timestamp).context(SaveDynDnsResultError)?;
|
write!(file, "{}", now_timestamp).map_err(|source| PeachError::Write {
|
||||||
|
source,
|
||||||
|
path: DYNDNS_LOG_PATH.to_string(),
|
||||||
|
})?;
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,12 +198,19 @@ pub fn get_num_seconds_since_successful_dns_update() -> Result<Option<i64>, Peac
|
|||||||
if !log_exists {
|
if !log_exists {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
} else {
|
} else {
|
||||||
let contents =
|
let contents = fs::read_to_string(DYNDNS_LOG_PATH).map_err(|source| PeachError::Read {
|
||||||
fs::read_to_string(DYNDNS_LOG_PATH).expect("Something went wrong reading the file");
|
source,
|
||||||
|
path: DYNDNS_LOG_PATH.to_string(),
|
||||||
|
})?;
|
||||||
// replace newline if found
|
// replace newline if found
|
||||||
|
// TODO: maybe we can use `.trim()` instead
|
||||||
let contents = contents.replace("\n", "");
|
let contents = contents.replace("\n", "");
|
||||||
let time_ran_dt = DateTime::parse_from_rfc3339(&contents).context(ChronoParseError {
|
// TODO: consider adding additional context?
|
||||||
msg: "Error parsing dyndns time from latest_result.log".to_string(),
|
let time_ran_dt = DateTime::parse_from_rfc3339(&contents).map_err(|source| {
|
||||||
|
PeachError::ParseDateTime {
|
||||||
|
source,
|
||||||
|
path: DYNDNS_LOG_PATH.to_string(),
|
||||||
|
}
|
||||||
})?;
|
})?;
|
||||||
let current_time: DateTime<Utc> = Utc::now();
|
let current_time: DateTime<Utc> = Utc::now();
|
||||||
let duration = current_time.signed_duration_since(time_ran_dt);
|
let duration = current_time.signed_duration_since(time_ran_dt);
|
||||||
@ -261,6 +259,7 @@ pub fn get_dyndns_subdomain(dyndns_full_domain: &str) -> Option<String> {
|
|||||||
|
|
||||||
// helper function which checks if a dyndns domain is new
|
// helper function which checks if a dyndns domain is new
|
||||||
pub fn check_is_new_dyndns_domain(dyndns_full_domain: &str) -> bool {
|
pub fn check_is_new_dyndns_domain(dyndns_full_domain: &str) -> bool {
|
||||||
|
// TODO: return `Result<bool, PeachError>` and replace `unwrap` with `?` operator
|
||||||
let peach_config = load_peach_config().unwrap();
|
let peach_config = load_peach_config().unwrap();
|
||||||
let previous_dyndns_domain = peach_config.dyn_domain;
|
let previous_dyndns_domain = peach_config.dyn_domain;
|
||||||
dyndns_full_domain != previous_dyndns_domain
|
dyndns_full_domain != previous_dyndns_domain
|
||||||
|
@ -1,136 +1,222 @@
|
|||||||
//! Basic error handling for the network, OLED, stats and dyndns JSON-RPC clients.
|
#![warn(missing_docs)]
|
||||||
pub use snafu::ResultExt;
|
|
||||||
use snafu::Snafu;
|
|
||||||
use std::error;
|
|
||||||
pub type BoxError = Box<dyn error::Error>;
|
|
||||||
|
|
||||||
#[derive(Debug, Snafu)]
|
//! Error handling for various aspects of the PeachCloud system, including the network, OLED, stats and dyndns JSON-RPC clients, as well as the configuration manager, sbot client and password utilities.
|
||||||
#[snafu(visibility(pub(crate)))]
|
|
||||||
|
use std::{io, str, string};
|
||||||
|
|
||||||
|
/// This type represents all possible errors that can occur when interacting with the PeachCloud library.
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum PeachError {
|
pub enum PeachError {
|
||||||
#[snafu(display("{}", source))]
|
/// Represents all other cases of `std::io::Error`.
|
||||||
JsonRpcHttp { source: jsonrpc_client_http::Error },
|
Io(io::Error),
|
||||||
#[snafu(display("{}", source))]
|
|
||||||
JsonRpcClientCore { source: jsonrpc_client_core::Error },
|
/// Represents a JSON-RPC core error returned from a JSON-RPC client.
|
||||||
#[snafu(display("{}", source))]
|
JsonRpcClientCore(jsonrpc_client_core::Error),
|
||||||
Serde { source: serde_json::error::Error },
|
|
||||||
#[snafu(display("{}", source))]
|
/// Represents a JSON-RPC core error returned from a JSON-RPC server.
|
||||||
PeachParseBoolError { source: std::str::ParseBoolError },
|
JsonRpcCore(jsonrpc_core::Error),
|
||||||
#[snafu(display("{}", source))]
|
|
||||||
SetConfigError { source: serde_yaml::Error },
|
/// Represents a JSON-RPC HTTP error returned from a JSON-RPC client.
|
||||||
#[snafu(display("Failed to read: {}", file))]
|
JsonRpcHttp(jsonrpc_client_http::Error),
|
||||||
ReadConfigError {
|
|
||||||
source: std::io::Error,
|
/// Represents a failure to update the nameserver.
|
||||||
file: String,
|
NsUpdate {
|
||||||
},
|
/// A message describing the context of the attempted nameserver update.
|
||||||
#[snafu(display("Failed to save: {}", file))]
|
|
||||||
WriteConfigError {
|
|
||||||
source: std::io::Error,
|
|
||||||
file: String,
|
|
||||||
},
|
|
||||||
#[snafu(display("Failed to save tsig key: {} {}", path, source))]
|
|
||||||
SaveTsigKeyError {
|
|
||||||
source: std::io::Error,
|
|
||||||
path: String,
|
|
||||||
},
|
|
||||||
#[snafu(display("{}", msg))]
|
|
||||||
NsUpdateError { msg: String },
|
|
||||||
#[snafu(display("Failed to run nsupdate: {}", source))]
|
|
||||||
NsCommandError { source: std::io::Error },
|
|
||||||
#[snafu(display("Failed to get public IP address: {}", source))]
|
|
||||||
GetPublicIpError { source: std::io::Error },
|
|
||||||
#[snafu(display("Failed to decode public ip: {}", source))]
|
|
||||||
DecodePublicIpError { source: std::str::Utf8Error },
|
|
||||||
#[snafu(display("Failed to decode nsupdate output: {}", source))]
|
|
||||||
DecodeNsUpdateOutputError { source: std::string::FromUtf8Error },
|
|
||||||
#[snafu(display("{}", source))]
|
|
||||||
YamlError { source: serde_yaml::Error },
|
|
||||||
#[snafu(display("{:?}", err))]
|
|
||||||
JsonRpcCore { err: jsonrpc_core::Error },
|
|
||||||
#[snafu(display("Error creating regex: {}", source))]
|
|
||||||
RegexError { source: regex::Error },
|
|
||||||
#[snafu(display("Failed to decode utf8: {}", source))]
|
|
||||||
FromUtf8Error { source: std::string::FromUtf8Error },
|
|
||||||
#[snafu(display("Encountered Utf8Error: {}", source))]
|
|
||||||
Utf8Error { source: std::str::Utf8Error },
|
|
||||||
#[snafu(display("Stdio error: {}: {}", msg, source))]
|
|
||||||
StdIoError { source: std::io::Error, msg: String },
|
|
||||||
#[snafu(display("Failed to parse time from {} {}", source, msg))]
|
|
||||||
ChronoParseError {
|
|
||||||
source: chrono::ParseError,
|
|
||||||
msg: String,
|
msg: String,
|
||||||
},
|
},
|
||||||
#[snafu(display("Failed to save dynamic dns success log: {}", source))]
|
|
||||||
SaveDynDnsResultError { source: std::io::Error },
|
/// Represents a failure to parse a string slice to a boolean value.
|
||||||
#[snafu(display("New passwords do not match"))]
|
ParseBool(str::ParseBoolError),
|
||||||
PasswordsDoNotMatch,
|
|
||||||
#[snafu(display("No admin password is set"))]
|
/// Represents a failure to parse a `DateTime`. Includes the error source and the file path
|
||||||
|
/// used in the parse attempt.
|
||||||
|
ParseDateTime {
|
||||||
|
/// The underlying source of the error.
|
||||||
|
source: chrono::ParseError,
|
||||||
|
/// The file path for the parse attempt.
|
||||||
|
path: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Represents the submission of an incorrect admin password.
|
||||||
|
PasswordIncorrect,
|
||||||
|
|
||||||
|
/// Represents the submission of two passwords which do not match.
|
||||||
|
PasswordMismatch,
|
||||||
|
|
||||||
|
/// Represents an unset admin password (empty password hash value) in the config file.
|
||||||
PasswordNotSet,
|
PasswordNotSet,
|
||||||
#[snafu(display("The supplied password was not correct"))]
|
|
||||||
InvalidPassword,
|
/// Represents a failure to read from input. Includes the error source and the file path used
|
||||||
#[snafu(display("Error saving new password: {}", msg))]
|
/// in the read attempt.
|
||||||
FailedToSetNewPassword { msg: String },
|
Read {
|
||||||
#[snafu(display("Error calling sbotcli: {}", msg))]
|
/// The underlying source of the error.
|
||||||
SbotCliError { msg: String },
|
source: io::Error,
|
||||||
#[snafu(display("Error deleting ssb admin id, id not found"))]
|
/// The file path for the read attempt.
|
||||||
SsbAdminIdNotFound { id: String },
|
path: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Represents a failure to parse or compile a regular expression.
|
||||||
|
Regex(regex::Error),
|
||||||
|
|
||||||
|
/// Represents a failure to successfully execute an sbot command.
|
||||||
|
SbotCli {
|
||||||
|
/// The `stderr` output from the sbot command.
|
||||||
|
msg: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Represents a failure to serialize or deserialize JSON.
|
||||||
|
SerdeJson(serde_json::error::Error),
|
||||||
|
|
||||||
|
/// Represents a failure to serialize or deserialize YAML.
|
||||||
|
SerdeYaml(serde_yaml::Error),
|
||||||
|
|
||||||
|
/// Represents a failure to find the given SSB ID in the config file.
|
||||||
|
SsbAdminIdNotFound {
|
||||||
|
/// An SSB ID (public key).
|
||||||
|
id: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Represents a failure to interpret a sequence of u8 as a string slice.
|
||||||
|
Utf8ToStr(str::Utf8Error),
|
||||||
|
|
||||||
|
/// Represents a failure to interpret a sequence of u8 as a String.
|
||||||
|
Utf8ToString(string::FromUtf8Error),
|
||||||
|
|
||||||
|
/// Represents a failure to write to output. Includes the error source and the file path used
|
||||||
|
/// in the write attempt.
|
||||||
|
Write {
|
||||||
|
/// The underlying source of the error.
|
||||||
|
source: io::Error,
|
||||||
|
/// The file path for the write attemp.
|
||||||
|
path: String,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<jsonrpc_client_http::Error> for PeachError {
|
impl std::error::Error for PeachError {
|
||||||
fn from(err: jsonrpc_client_http::Error) -> PeachError {
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||||
PeachError::JsonRpcHttp { source: err }
|
match *self {
|
||||||
|
PeachError::Io(_) => None,
|
||||||
|
PeachError::JsonRpcClientCore(_) => None,
|
||||||
|
PeachError::JsonRpcCore(_) => None,
|
||||||
|
PeachError::JsonRpcHttp(_) => None,
|
||||||
|
PeachError::NsUpdate { .. } => None,
|
||||||
|
PeachError::ParseBool(_) => None,
|
||||||
|
PeachError::ParseDateTime { ref source, .. } => Some(source),
|
||||||
|
PeachError::PasswordIncorrect => None,
|
||||||
|
PeachError::PasswordMismatch => None,
|
||||||
|
PeachError::PasswordNotSet => None,
|
||||||
|
PeachError::Read { ref source, .. } => Some(source),
|
||||||
|
PeachError::Regex(_) => None,
|
||||||
|
PeachError::SbotCli { .. } => None,
|
||||||
|
PeachError::SerdeJson(_) => None,
|
||||||
|
PeachError::SerdeYaml(_) => None,
|
||||||
|
PeachError::SsbAdminIdNotFound { .. } => None,
|
||||||
|
PeachError::Utf8ToStr(_) => None,
|
||||||
|
PeachError::Utf8ToString(_) => None,
|
||||||
|
PeachError::Write { ref source, .. } => Some(source),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<jsonrpc_client_core::Error> for PeachError {
|
impl std::fmt::Display for PeachError {
|
||||||
fn from(err: jsonrpc_client_core::Error) -> PeachError {
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
PeachError::JsonRpcClientCore { source: err }
|
match *self {
|
||||||
}
|
PeachError::Io(ref err) => err.fmt(f),
|
||||||
}
|
PeachError::JsonRpcClientCore(ref err) => err.fmt(f),
|
||||||
|
PeachError::JsonRpcCore(ref err) => {
|
||||||
impl From<serde_json::error::Error> for PeachError {
|
write!(f, "{:?}", err)
|
||||||
fn from(err: serde_json::error::Error) -> PeachError {
|
}
|
||||||
PeachError::Serde { source: err }
|
PeachError::JsonRpcHttp(ref err) => err.fmt(f),
|
||||||
}
|
PeachError::NsUpdate { ref msg } => {
|
||||||
}
|
write!(f, "Nameserver error: {}", msg)
|
||||||
|
}
|
||||||
impl From<serde_yaml::Error> for PeachError {
|
PeachError::ParseBool(ref err) => err.fmt(f),
|
||||||
fn from(err: serde_yaml::Error) -> PeachError {
|
PeachError::ParseDateTime { ref path, .. } => {
|
||||||
PeachError::YamlError { source: err }
|
write!(f, "Date/time parse error: {}", path)
|
||||||
|
}
|
||||||
|
PeachError::PasswordIncorrect => {
|
||||||
|
write!(f, "Password error: user-supplied password is incorrect")
|
||||||
|
}
|
||||||
|
PeachError::PasswordMismatch => {
|
||||||
|
write!(f, "Password error: user-supplied passwords do not match")
|
||||||
|
}
|
||||||
|
PeachError::PasswordNotSet => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"Password error: hash value in YAML configuration file is empty"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
PeachError::Read { ref path, .. } => {
|
||||||
|
write!(f, "Read error: {}", path)
|
||||||
|
}
|
||||||
|
PeachError::Regex(ref err) => err.fmt(f),
|
||||||
|
PeachError::SbotCli { ref msg } => {
|
||||||
|
write!(f, "Sbot error: {}", msg)
|
||||||
|
}
|
||||||
|
PeachError::SerdeJson(ref err) => err.fmt(f),
|
||||||
|
PeachError::SerdeYaml(ref err) => err.fmt(f),
|
||||||
|
PeachError::SsbAdminIdNotFound { ref id } => {
|
||||||
|
write!(f, "Config error: SSB admin ID `{}` not found", id)
|
||||||
|
}
|
||||||
|
PeachError::Utf8ToStr(ref err) => err.fmt(f),
|
||||||
|
PeachError::Utf8ToString(ref err) => err.fmt(f),
|
||||||
|
PeachError::Write { ref path, .. } => {
|
||||||
|
write!(f, "Write error: {}", path)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<std::io::Error> for PeachError {
|
impl From<std::io::Error> for PeachError {
|
||||||
fn from(err: std::io::Error) -> PeachError {
|
fn from(err: std::io::Error) -> PeachError {
|
||||||
PeachError::StdIoError {
|
PeachError::Io(err)
|
||||||
source: err,
|
}
|
||||||
msg: "".to_string(),
|
}
|
||||||
}
|
|
||||||
|
impl From<jsonrpc_client_core::Error> for PeachError {
|
||||||
|
fn from(err: jsonrpc_client_core::Error) -> PeachError {
|
||||||
|
PeachError::JsonRpcClientCore(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<jsonrpc_client_http::Error> for PeachError {
|
||||||
|
fn from(err: jsonrpc_client_http::Error) -> PeachError {
|
||||||
|
PeachError::JsonRpcHttp(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<str::ParseBoolError> for PeachError {
|
||||||
|
fn from(err: str::ParseBoolError) -> PeachError {
|
||||||
|
PeachError::ParseBool(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<regex::Error> for PeachError {
|
impl From<regex::Error> for PeachError {
|
||||||
fn from(err: regex::Error) -> PeachError {
|
fn from(err: regex::Error) -> PeachError {
|
||||||
PeachError::RegexError { source: err }
|
PeachError::Regex(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<std::string::FromUtf8Error> for PeachError {
|
impl From<serde_json::error::Error> for PeachError {
|
||||||
fn from(err: std::string::FromUtf8Error) -> PeachError {
|
fn from(err: serde_json::error::Error) -> PeachError {
|
||||||
PeachError::FromUtf8Error { source: err }
|
PeachError::SerdeJson(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<std::str::Utf8Error> for PeachError {
|
impl From<serde_yaml::Error> for PeachError {
|
||||||
fn from(err: std::str::Utf8Error) -> PeachError {
|
fn from(err: serde_yaml::Error) -> PeachError {
|
||||||
PeachError::Utf8Error { source: err }
|
PeachError::SerdeYaml(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<chrono::ParseError> for PeachError {
|
impl From<str::Utf8Error> for PeachError {
|
||||||
fn from(err: chrono::ParseError) -> PeachError {
|
fn from(err: str::Utf8Error) -> PeachError {
|
||||||
PeachError::ChronoParseError {
|
PeachError::Utf8ToStr(err)
|
||||||
source: err,
|
}
|
||||||
msg: "".to_string(),
|
}
|
||||||
}
|
|
||||||
|
impl From<string::FromUtf8Error> for PeachError {
|
||||||
|
fn from(err: string::FromUtf8Error) -> PeachError {
|
||||||
|
PeachError::Utf8ToString(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,3 @@
|
|||||||
// this is to ignore a clippy warning that suggests
|
|
||||||
// to replace code with the same code that is already there (possibly a bug)
|
|
||||||
#![allow(clippy::nonstandard_macro_braces)]
|
|
||||||
|
|
||||||
pub mod config_manager;
|
pub mod config_manager;
|
||||||
pub mod dyndns_client;
|
pub mod dyndns_client;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
@ -9,9 +9,6 @@
|
|||||||
//! Several helper methods are also included here which bundle multiple client
|
//! Several helper methods are also included here which bundle multiple client
|
||||||
//! calls to achieve the desired functionality.
|
//! calls to achieve the desired functionality.
|
||||||
|
|
||||||
// TODO: fix these clippy errors so this allow can be removed
|
|
||||||
#![allow(clippy::needless_borrow)]
|
|
||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
use jsonrpc_client_core::{expand_params, jsonrpc_client};
|
use jsonrpc_client_core::{expand_params, jsonrpc_client};
|
||||||
@ -166,9 +163,9 @@ pub fn disable(iface: &str, ssid: &str) -> std::result::Result<String, PeachErro
|
|||||||
let mut client = PeachNetworkClient::new(transport_handle);
|
let mut client = PeachNetworkClient::new(transport_handle);
|
||||||
|
|
||||||
info!("Performing id call to peach-network microservice.");
|
info!("Performing id call to peach-network microservice.");
|
||||||
let id = client.id(&iface, &ssid).call()?;
|
let id = client.id(iface, ssid).call()?;
|
||||||
info!("Performing disable call to peach-network microservice.");
|
info!("Performing disable call to peach-network microservice.");
|
||||||
client.disable(&id, &iface).call()?;
|
client.disable(&id, iface).call()?;
|
||||||
|
|
||||||
let response = "success".to_string();
|
let response = "success".to_string();
|
||||||
|
|
||||||
@ -194,12 +191,12 @@ pub fn forget(iface: &str, ssid: &str) -> std::result::Result<String, PeachError
|
|||||||
let mut client = PeachNetworkClient::new(transport_handle);
|
let mut client = PeachNetworkClient::new(transport_handle);
|
||||||
|
|
||||||
info!("Performing id call to peach-network microservice.");
|
info!("Performing id call to peach-network microservice.");
|
||||||
let id = client.id(&iface, &ssid).call()?;
|
let id = client.id(iface, ssid).call()?;
|
||||||
info!("Performing delete call to peach-network microservice.");
|
info!("Performing delete call to peach-network microservice.");
|
||||||
// WEIRD BUG: the parameters below are technically in the wrong order:
|
// WEIRD BUG: the parameters below are technically in the wrong order:
|
||||||
// it should be id first and then iface, but somehow they get twisted.
|
// it should be id first and then iface, but somehow they get twisted.
|
||||||
// i don't understand computers.
|
// i don't understand computers.
|
||||||
client.delete(&iface, &id).call()?;
|
client.delete(iface, &id).call()?;
|
||||||
info!("Performing save call to peach-network microservice.");
|
info!("Performing save call to peach-network microservice.");
|
||||||
client.save().call()?;
|
client.save().call()?;
|
||||||
|
|
||||||
@ -357,8 +354,7 @@ pub fn saved_ap(ssid: &str) -> std::result::Result<bool, PeachError> {
|
|||||||
// retrieve a list of access points with saved credentials
|
// retrieve a list of access points with saved credentials
|
||||||
let saved_aps = match client.saved_networks().call() {
|
let saved_aps = match client.saved_networks().call() {
|
||||||
Ok(ssids) => {
|
Ok(ssids) => {
|
||||||
let networks: Vec<Networks> = serde_json::from_str(ssids.as_str())
|
let networks: Vec<Networks> = serde_json::from_str(ssids.as_str())?;
|
||||||
.expect("Failed to deserialize saved_networks response");
|
|
||||||
networks
|
networks
|
||||||
}
|
}
|
||||||
// return an empty vector if there are no saved access point credentials
|
// return an empty vector if there are no saved access point credentials
|
||||||
@ -479,7 +475,7 @@ pub fn traffic(iface: &str) -> std::result::Result<Traffic, PeachError> {
|
|||||||
let mut client = PeachNetworkClient::new(transport_handle);
|
let mut client = PeachNetworkClient::new(transport_handle);
|
||||||
|
|
||||||
let response = client.traffic(iface).call()?;
|
let response = client.traffic(iface).call()?;
|
||||||
let t: Traffic = serde_json::from_str(&response).unwrap();
|
let t: Traffic = serde_json::from_str(&response)?;
|
||||||
|
|
||||||
Ok(t)
|
Ok(t)
|
||||||
}
|
}
|
||||||
@ -506,13 +502,13 @@ pub fn update(iface: &str, ssid: &str, pass: &str) -> std::result::Result<String
|
|||||||
|
|
||||||
// get the id of the network
|
// get the id of the network
|
||||||
info!("Performing id call to peach-network microservice.");
|
info!("Performing id call to peach-network microservice.");
|
||||||
let id = client.id(&iface, &ssid).call()?;
|
let id = client.id(iface, ssid).call()?;
|
||||||
// delete the old credentials
|
// delete the old credentials
|
||||||
// WEIRD BUG: the parameters below are technically in the wrong order:
|
// WEIRD BUG: the parameters below are technically in the wrong order:
|
||||||
// it should be id first and then iface, but somehow they get twisted.
|
// it should be id first and then iface, but somehow they get twisted.
|
||||||
// i don't understand computers.
|
// i don't understand computers.
|
||||||
info!("Performing delete call to peach-network microservice.");
|
info!("Performing delete call to peach-network microservice.");
|
||||||
client.delete(&iface, &id).call()?;
|
client.delete(iface, &id).call()?;
|
||||||
// save the updates to wpa_supplicant.conf
|
// save the updates to wpa_supplicant.conf
|
||||||
info!("Performing save call to peach-network microservice.");
|
info!("Performing save call to peach-network microservice.");
|
||||||
client.save().call()?;
|
client.save().call()?;
|
||||||
|
@ -1,23 +1,19 @@
|
|||||||
use crate::config_manager::{get_peachcloud_domain, load_peach_config,
|
|
||||||
set_admin_password_hash, get_admin_password_hash,
|
|
||||||
get_temporary_password_hash, set_temporary_password_hash};
|
|
||||||
use crate::error::PeachError;
|
|
||||||
use crate::sbot_client;
|
|
||||||
use rand::distributions::Alphanumeric;
|
|
||||||
use rand::{thread_rng, Rng};
|
|
||||||
use std::iter;
|
use std::iter;
|
||||||
use crypto::digest::Digest;
|
|
||||||
use crypto::sha3::Sha3;
|
use crypto::{digest::Digest, sha3::Sha3};
|
||||||
|
use rand::{distributions::Alphanumeric, thread_rng, Rng};
|
||||||
|
|
||||||
|
use crate::{config_manager, error::PeachError, sbot_client};
|
||||||
|
|
||||||
/// Returns Ok(()) if the supplied password is correct,
|
/// Returns Ok(()) if the supplied password is correct,
|
||||||
/// and returns Err if the supplied password is incorrect.
|
/// and returns Err if the supplied password is incorrect.
|
||||||
pub fn verify_password(password: &str) -> Result<(), PeachError> {
|
pub fn verify_password(password: &str) -> Result<(), PeachError> {
|
||||||
let real_admin_password_hash = get_admin_password_hash()?;
|
let real_admin_password_hash = config_manager::get_admin_password_hash()?;
|
||||||
let password_hash = hash_password(&password.to_string());
|
let password_hash = hash_password(&password.to_string());
|
||||||
if real_admin_password_hash == password_hash {
|
if real_admin_password_hash == password_hash {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(PeachError::InvalidPassword)
|
Err(PeachError::PasswordIncorrect)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,22 +25,16 @@ pub fn validate_new_passwords(new_password1: &str, new_password2: &str) -> Resul
|
|||||||
if new_password1 == new_password2 {
|
if new_password1 == new_password2 {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(PeachError::PasswordsDoNotMatch)
|
Err(PeachError::PasswordMismatch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets a new password for the admin user
|
/// Sets a new password for the admin user
|
||||||
pub fn set_new_password(new_password: &str) -> Result<(), PeachError> {
|
pub fn set_new_password(new_password: &str) -> Result<(), PeachError> {
|
||||||
let new_password_hash = hash_password(&new_password.to_string());
|
let new_password_hash = hash_password(&new_password.to_string());
|
||||||
let result = set_admin_password_hash(&new_password_hash);
|
config_manager::set_admin_password_hash(&new_password_hash)?;
|
||||||
match result {
|
|
||||||
Ok(_) => {
|
Ok(())
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
Err(_err) => {
|
|
||||||
Err(PeachError::FailedToSetNewPassword { msg: "failed to save password hash".to_string() })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a hash from a password string
|
/// Creates a hash from a password string
|
||||||
@ -58,26 +48,20 @@ pub fn hash_password(password: &str) -> String {
|
|||||||
/// which can be used to reset the permanent password
|
/// which can be used to reset the permanent password
|
||||||
pub fn set_new_temporary_password(new_password: &str) -> Result<(), PeachError> {
|
pub fn set_new_temporary_password(new_password: &str) -> Result<(), PeachError> {
|
||||||
let new_password_hash = hash_password(&new_password.to_string());
|
let new_password_hash = hash_password(&new_password.to_string());
|
||||||
let result = set_temporary_password_hash(&new_password_hash);
|
config_manager::set_temporary_password_hash(&new_password_hash)?;
|
||||||
match result {
|
|
||||||
Ok(_) => {
|
Ok(())
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
Err(_err) => {
|
|
||||||
Err(PeachError::FailedToSetNewPassword { msg: "failed to save temporary password hash".to_string() })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns Ok(()) if the supplied temp_password is correct,
|
/// Returns Ok(()) if the supplied temp_password is correct,
|
||||||
/// and returns Err if the supplied temp_password is incorrect
|
/// and returns Err if the supplied temp_password is incorrect
|
||||||
pub fn verify_temporary_password(password: &str) -> Result<(), PeachError> {
|
pub fn verify_temporary_password(password: &str) -> Result<(), PeachError> {
|
||||||
let temporary_admin_password_hash = get_temporary_password_hash()?;
|
let temporary_admin_password_hash = config_manager::get_temporary_password_hash()?;
|
||||||
let password_hash = hash_password(&password.to_string());
|
let password_hash = hash_password(&password.to_string());
|
||||||
if temporary_admin_password_hash == password_hash {
|
if temporary_admin_password_hash == password_hash {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(PeachError::InvalidPassword)
|
Err(PeachError::PasswordIncorrect)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,7 +77,7 @@ pub fn send_password_reset() -> Result<(), PeachError> {
|
|||||||
.collect();
|
.collect();
|
||||||
// save this string as a new temporary password
|
// save this string as a new temporary password
|
||||||
set_new_temporary_password(&temporary_password)?;
|
set_new_temporary_password(&temporary_password)?;
|
||||||
let domain = get_peachcloud_domain()?;
|
let domain = config_manager::get_peachcloud_domain()?;
|
||||||
|
|
||||||
// then send temporary password as a private ssb message to admin
|
// then send temporary password as a private ssb message to admin
|
||||||
let mut msg = format!(
|
let mut msg = format!(
|
||||||
@ -117,7 +101,7 @@ using this link: http://peach.local/reset_password",
|
|||||||
};
|
};
|
||||||
msg += &remote_link;
|
msg += &remote_link;
|
||||||
// finally send the message to the admins
|
// finally send the message to the admins
|
||||||
let peach_config = load_peach_config()?;
|
let peach_config = config_manager::load_peach_config()?;
|
||||||
for ssb_admin_id in peach_config.ssb_admin_ids {
|
for ssb_admin_id in peach_config.ssb_admin_ids {
|
||||||
sbot_client::private_message(&msg, &ssb_admin_id)?;
|
sbot_client::private_message(&msg, &ssb_admin_id)?;
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
//! Interfaces for monitoring and configuring go-sbot using sbotcli.
|
//! Interfaces for monitoring and configuring go-sbot using sbotcli.
|
||||||
//!
|
|
||||||
use crate::error::PeachError;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::error::PeachError;
|
||||||
|
|
||||||
pub fn is_sbot_online() -> Result<bool, PeachError> {
|
pub fn is_sbot_online() -> Result<bool, PeachError> {
|
||||||
let output = Command::new("/usr/bin/systemctl")
|
let output = Command::new("/usr/bin/systemctl")
|
||||||
.arg("status")
|
.arg("status")
|
||||||
@ -36,7 +38,7 @@ pub fn post(msg: &str) -> Result<(), PeachError> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
let stderr = std::str::from_utf8(&output.stderr)?;
|
let stderr = std::str::from_utf8(&output.stderr)?;
|
||||||
Err(PeachError::SbotCliError {
|
Err(PeachError::SbotCli {
|
||||||
msg: format!("Error making ssb post: {}", stderr),
|
msg: format!("Error making ssb post: {}", stderr),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -83,7 +85,7 @@ pub fn update_pub_name(new_name: &str) -> Result<(), PeachError> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
let stderr = std::str::from_utf8(&output.stderr)?;
|
let stderr = std::str::from_utf8(&output.stderr)?;
|
||||||
Err(PeachError::SbotCliError {
|
Err(PeachError::SbotCli {
|
||||||
msg: format!("Error updating pub name: {}", stderr),
|
msg: format!("Error updating pub name: {}", stderr),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -102,7 +104,7 @@ pub fn private_message(msg: &str, recipient: &str) -> Result<(), PeachError> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
let stderr = std::str::from_utf8(&output.stderr)?;
|
let stderr = std::str::from_utf8(&output.stderr)?;
|
||||||
Err(PeachError::SbotCliError {
|
Err(PeachError::SbotCli {
|
||||||
msg: format!("Error sending ssb private message: {}", stderr),
|
msg: format!("Error sending ssb private message: {}", stderr),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "peach-oled"
|
name = "peach-oled"
|
||||||
version = "0.1.4"
|
version = "0.1.3"
|
||||||
authors = ["Andrew Reid <gnomad@cryptolab.net>"]
|
authors = ["Andrew Reid <gnomad@cryptolab.net>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
description = "Write and draw to OLED display using JSON-RPC over HTTP."
|
description = "Write and draw to OLED display using JSON-RPC over HTTP."
|
||||||
@ -27,16 +27,18 @@ travis-ci = { repository = "peachcloud/peach-oled", branch = "master" }
|
|||||||
maintenance = { status = "actively-developed" }
|
maintenance = { status = "actively-developed" }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embedded-graphics = "0.4.7"
|
jsonrpc-core = "11.0.0"
|
||||||
env_logger = "0.9"
|
jsonrpc-http-server = "11.0.0"
|
||||||
jsonrpc-core = "18"
|
|
||||||
jsonrpc-http-server = "18"
|
|
||||||
linux-embedded-hal = "0.2.2"
|
linux-embedded-hal = "0.2.2"
|
||||||
log = "0.4"
|
embedded-graphics = "0.4.7"
|
||||||
serde = { version = "1", features = ["derive"] }
|
|
||||||
nix="0.11"
|
|
||||||
ssd1306 = "0.2.6"
|
|
||||||
tinybmp = "0.1.0"
|
tinybmp = "0.1.0"
|
||||||
|
ssd1306 = "0.2.6"
|
||||||
|
serde = { version = "1.0.87", features = ["derive"] }
|
||||||
|
serde_json = "1.0.39"
|
||||||
|
log = "0.4.0"
|
||||||
|
env_logger = "0.6.1"
|
||||||
|
snafu = "0.4.1"
|
||||||
|
nix="0.11"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
jsonrpc-test = "18"
|
jsonrpc-test = "11.0.0"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# peach-oled
|
# peach-oled
|
||||||
|
|
||||||
[](https://travis-ci.com/peachcloud/peach-oled) 
|
[](https://travis-ci.com/peachcloud/peach-oled) 
|
||||||
|
|
||||||
OLED microservice module for PeachCloud. Write to a 128x64 OLED display with SDD1306 driver (I2C) using [JSON-RPC](https://www.jsonrpc.org/specification) over http.
|
OLED microservice module for PeachCloud. Write to a 128x64 OLED display with SDD1306 driver (I2C) using [JSON-RPC](https://www.jsonrpc.org/specification) over http.
|
||||||
|
|
||||||
|
@ -1,68 +1,44 @@
|
|||||||
use std::{error, fmt};
|
use std::error;
|
||||||
|
|
||||||
use jsonrpc_core::types::error::Error as JsonRpcError;
|
use jsonrpc_core::{types::error::Error, ErrorCode};
|
||||||
use jsonrpc_core::ErrorCode;
|
use linux_embedded_hal as hal;
|
||||||
use linux_embedded_hal::i2cdev::linux::LinuxI2CError;
|
use snafu::Snafu;
|
||||||
|
|
||||||
#[derive(Debug)]
|
pub type BoxError = Box<dyn error::Error>;
|
||||||
|
|
||||||
|
#[derive(Debug, Snafu)]
|
||||||
|
#[snafu(visibility(pub(crate)))]
|
||||||
pub enum OledError {
|
pub enum OledError {
|
||||||
|
#[snafu(display("Failed to create interface for I2C device: {}", source))]
|
||||||
I2CError {
|
I2CError {
|
||||||
source: LinuxI2CError,
|
source: hal::i2cdev::linux::LinuxI2CError,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#[snafu(display("Coordinate {} out of range {}: {}", coord, range, value))]
|
||||||
InvalidCoordinate {
|
InvalidCoordinate {
|
||||||
coord: String,
|
coord: String,
|
||||||
range: String,
|
range: String,
|
||||||
value: i32,
|
value: i32,
|
||||||
},
|
},
|
||||||
InvalidFontSize {
|
|
||||||
font: String,
|
// TODO: implement for validate() in src/lib.rs
|
||||||
},
|
#[snafu(display("Font size invalid: {}", font))]
|
||||||
InvalidString {
|
InvalidFontSize { font: String },
|
||||||
len: usize,
|
|
||||||
},
|
#[snafu(display("String length out of range 0-21: {}", len))]
|
||||||
MissingParameter {
|
InvalidString { len: usize },
|
||||||
source: JsonRpcError,
|
|
||||||
},
|
#[snafu(display("Missing expected parameter: {}", e))]
|
||||||
ParseError {
|
MissingParameter { e: Error },
|
||||||
source: JsonRpcError,
|
|
||||||
},
|
#[snafu(display("Failed to parse parameter: {}", e))]
|
||||||
|
ParseError { e: Error },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl error::Error for OledError {}
|
impl From<OledError> for Error {
|
||||||
|
|
||||||
impl fmt::Display for OledError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match *self {
|
|
||||||
OledError::ParseError { ref source } => {
|
|
||||||
write!(f, "Failed to parse parameter: {}", source)
|
|
||||||
}
|
|
||||||
OledError::MissingParameter { ref source } => {
|
|
||||||
write!(f, "Missing expected parameter: {}", source)
|
|
||||||
}
|
|
||||||
OledError::InvalidString { len } => {
|
|
||||||
write!(f, "String length out of range 0-21: {}", len)
|
|
||||||
}
|
|
||||||
OledError::InvalidFontSize { ref font } => {
|
|
||||||
write!(f, "Invalid font size: {}", font)
|
|
||||||
}
|
|
||||||
OledError::InvalidCoordinate {
|
|
||||||
ref coord,
|
|
||||||
ref range,
|
|
||||||
value,
|
|
||||||
} => {
|
|
||||||
write!(f, "Coordinate {} out of range {}: {}", coord, range, value)
|
|
||||||
}
|
|
||||||
OledError::I2CError { ref source } => {
|
|
||||||
write!(f, "Failed to create interface for I2C device: {}", source)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<OledError> for JsonRpcError {
|
|
||||||
fn from(err: OledError) -> Self {
|
fn from(err: OledError) -> Self {
|
||||||
match &err {
|
match &err {
|
||||||
OledError::I2CError { source } => JsonRpcError {
|
OledError::I2CError { source } => Error {
|
||||||
code: ErrorCode::ServerError(-32000),
|
code: ErrorCode::ServerError(-32000),
|
||||||
message: format!("Failed to create interface for I2C device: {}", source),
|
message: format!("Failed to create interface for I2C device: {}", source),
|
||||||
data: None,
|
data: None,
|
||||||
@ -71,7 +47,7 @@ impl From<OledError> for JsonRpcError {
|
|||||||
coord,
|
coord,
|
||||||
value,
|
value,
|
||||||
range,
|
range,
|
||||||
} => JsonRpcError {
|
} => Error {
|
||||||
code: ErrorCode::ServerError(-32001),
|
code: ErrorCode::ServerError(-32001),
|
||||||
message: format!(
|
message: format!(
|
||||||
"Validation error: coordinate {} out of range {}: {}",
|
"Validation error: coordinate {} out of range {}: {}",
|
||||||
@ -79,18 +55,18 @@ impl From<OledError> for JsonRpcError {
|
|||||||
),
|
),
|
||||||
data: None,
|
data: None,
|
||||||
},
|
},
|
||||||
OledError::InvalidFontSize { font } => JsonRpcError {
|
OledError::InvalidFontSize { font } => Error {
|
||||||
code: ErrorCode::ServerError(-32002),
|
code: ErrorCode::ServerError(-32002),
|
||||||
message: format!("Validation error: {} is not an accepted font size. Use 6x8, 6x12, 8x16 or 12x16 instead", font),
|
message: format!("Validation error: {} is not an accepted font size. Use 6x8, 6x12, 8x16 or 12x16 instead", font),
|
||||||
data: None,
|
data: None,
|
||||||
},
|
},
|
||||||
OledError::InvalidString { len } => JsonRpcError {
|
OledError::InvalidString { len } => Error {
|
||||||
code: ErrorCode::ServerError(-32003),
|
code: ErrorCode::ServerError(-32003),
|
||||||
message: format!("Validation error: string length {} out of range 0-21", len),
|
message: format!("Validation error: string length {} out of range 0-21", len),
|
||||||
data: None,
|
data: None,
|
||||||
},
|
},
|
||||||
OledError::MissingParameter { source } => source.clone(),
|
OledError::MissingParameter { e } => e.clone(),
|
||||||
OledError::ParseError { source } => source.clone(),
|
OledError::ParseError { e } => e.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,23 +6,23 @@ use std::{
|
|||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
};
|
};
|
||||||
|
|
||||||
use embedded_graphics::{
|
use embedded_graphics::coord::Coord;
|
||||||
coord::Coord,
|
use embedded_graphics::fonts::{Font12x16, Font6x12, Font6x8, Font8x16};
|
||||||
fonts::{Font12x16, Font6x12, Font6x8, Font8x16},
|
use embedded_graphics::image::Image1BPP;
|
||||||
image::Image1BPP,
|
use embedded_graphics::prelude::*;
|
||||||
prelude::*,
|
|
||||||
};
|
|
||||||
use hal::I2cdev;
|
use hal::I2cdev;
|
||||||
use jsonrpc_core::{types::error::Error, IoHandler, Params, Value};
|
use jsonrpc_core::{types::error::Error, IoHandler, Params, Value};
|
||||||
use jsonrpc_http_server::{AccessControlAllowOrigin, DomainsValidation, ServerBuilder};
|
use jsonrpc_http_server::{AccessControlAllowOrigin, DomainsValidation, ServerBuilder};
|
||||||
use linux_embedded_hal as hal;
|
use linux_embedded_hal as hal;
|
||||||
use log::{debug, error, info};
|
use log::{debug, error, info};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use ssd1306::{prelude::*, Builder};
|
use snafu::{ensure, ResultExt};
|
||||||
|
use ssd1306::prelude::*;
|
||||||
|
use ssd1306::Builder;
|
||||||
|
|
||||||
use crate::error::OledError;
|
use crate::error::{BoxError, I2CError, InvalidCoordinate, InvalidString, OledError};
|
||||||
|
|
||||||
// define the Graphic struct for receiving draw commands
|
//define the Graphic struct for receiving draw commands
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct Graphic {
|
pub struct Graphic {
|
||||||
bytes: Vec<u8>,
|
bytes: Vec<u8>,
|
||||||
@ -32,7 +32,7 @@ pub struct Graphic {
|
|||||||
y_coord: i32,
|
y_coord: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
// define the Msg struct for receiving write commands
|
//define the Msg struct for receiving write commands
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct Msg {
|
pub struct Msg {
|
||||||
x_coord: i32,
|
x_coord: i32,
|
||||||
@ -41,61 +41,86 @@ pub struct Msg {
|
|||||||
font_size: String,
|
font_size: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
// definte the On struct for receiving power on/off commands
|
//definte the On struct for receiving power on/off commands
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct On {
|
pub struct On {
|
||||||
on: bool,
|
on: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate(msg: &Msg) -> Result<(), OledError> {
|
fn validate(m: &Msg) -> Result<(), OledError> {
|
||||||
if msg.string.len() > 21 {
|
ensure!(
|
||||||
Err(OledError::InvalidString {
|
m.string.len() <= 21,
|
||||||
len: msg.string.len(),
|
InvalidString {
|
||||||
})
|
len: m.string.len()
|
||||||
} else if msg.x_coord < 0 || msg.x_coord > 128 {
|
}
|
||||||
Err(OledError::InvalidCoordinate {
|
);
|
||||||
|
|
||||||
|
ensure!(
|
||||||
|
m.x_coord >= 0,
|
||||||
|
InvalidCoordinate {
|
||||||
coord: "x".to_string(),
|
coord: "x".to_string(),
|
||||||
range: "0-128".to_string(),
|
range: "0-128".to_string(),
|
||||||
value: msg.x_coord,
|
value: m.x_coord,
|
||||||
})
|
}
|
||||||
} else if msg.y_coord < 0 || msg.y_coord > 147 {
|
);
|
||||||
Err(OledError::InvalidCoordinate {
|
|
||||||
|
ensure!(
|
||||||
|
m.x_coord < 129,
|
||||||
|
InvalidCoordinate {
|
||||||
|
coord: "x".to_string(),
|
||||||
|
range: "0-128".to_string(),
|
||||||
|
value: m.x_coord,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
ensure!(
|
||||||
|
m.y_coord >= 0,
|
||||||
|
InvalidCoordinate {
|
||||||
coord: "y".to_string(),
|
coord: "y".to_string(),
|
||||||
range: "0-47".to_string(),
|
range: "0-47".to_string(),
|
||||||
value: msg.y_coord,
|
value: m.y_coord,
|
||||||
})
|
}
|
||||||
} else {
|
);
|
||||||
Ok(())
|
|
||||||
}
|
ensure!(
|
||||||
|
m.y_coord < 148,
|
||||||
|
InvalidCoordinate {
|
||||||
|
coord: "y".to_string(),
|
||||||
|
range: "0-47".to_string(),
|
||||||
|
value: m.y_coord,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run() -> Result<(), OledError> {
|
pub fn run() -> Result<(), BoxError> {
|
||||||
info!("Starting up.");
|
info!("Starting up.");
|
||||||
|
|
||||||
debug!("Creating interface for I2C device.");
|
debug!("Creating interface for I2C device.");
|
||||||
let i2c = I2cdev::new("/dev/i2c-1").map_err(|source| OledError::I2CError { source })?;
|
let i2c = I2cdev::new("/dev/i2c-1").context(I2CError)?;
|
||||||
|
|
||||||
let mut display: GraphicsMode<_> = Builder::new().connect_i2c(i2c).into();
|
let mut disp: GraphicsMode<_> = Builder::new().connect_i2c(i2c).into();
|
||||||
|
|
||||||
info!("Initializing the display.");
|
info!("Initializing the display.");
|
||||||
display.init().unwrap_or_else(|_| {
|
disp.init().unwrap_or_else(|_| {
|
||||||
error!("Problem initializing the OLED display.");
|
error!("Problem initializing the OLED display.");
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
debug!("Flushing the display.");
|
debug!("Flushing the display.");
|
||||||
display.flush().unwrap_or_else(|_| {
|
disp.flush().unwrap_or_else(|_| {
|
||||||
error!("Problem flushing the OLED display.");
|
error!("Problem flushing the OLED display.");
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
let oled = Arc::new(Mutex::new(display));
|
let oled = Arc::new(Mutex::new(disp));
|
||||||
let oled_clone = Arc::clone(&oled);
|
let oled_clone = Arc::clone(&oled);
|
||||||
|
|
||||||
info!("Creating JSON-RPC I/O handler.");
|
info!("Creating JSON-RPC I/O handler.");
|
||||||
let mut io = IoHandler::default();
|
let mut io = IoHandler::default();
|
||||||
|
|
||||||
io.add_sync_method("clear", move |_| {
|
io.add_method("clear", move |_| {
|
||||||
let mut oled = oled_clone.lock().unwrap();
|
let mut oled = oled_clone.lock().unwrap();
|
||||||
info!("Clearing the display.");
|
info!("Clearing the display.");
|
||||||
oled.clear();
|
oled.clear();
|
||||||
@ -109,20 +134,21 @@ pub fn run() -> Result<(), OledError> {
|
|||||||
|
|
||||||
let oled_clone = Arc::clone(&oled);
|
let oled_clone = Arc::clone(&oled);
|
||||||
|
|
||||||
io.add_sync_method("draw", move |params: Params| {
|
io.add_method("draw", move |params: Params| {
|
||||||
let graphic: Graphic = params.parse()?;
|
let g: Result<Graphic, Error> = params.parse();
|
||||||
|
let g: Graphic = g?;
|
||||||
// TODO: add simple byte validation function
|
// TODO: add simple byte validation function
|
||||||
let mut oled = oled_clone.lock().unwrap();
|
let mut oled = oled_clone.lock().unwrap();
|
||||||
info!("Drawing image to the display.");
|
info!("Drawing image to the display.");
|
||||||
let image = Image1BPP::new(&graphic.bytes, graphic.width, graphic.height)
|
let im =
|
||||||
.translate(Coord::new(graphic.x_coord, graphic.y_coord));
|
Image1BPP::new(&g.bytes, g.width, g.height).translate(Coord::new(g.x_coord, g.y_coord));
|
||||||
oled.draw(image.into_iter());
|
oled.draw(im.into_iter());
|
||||||
Ok(Value::String("success".into()))
|
Ok(Value::String("success".into()))
|
||||||
});
|
});
|
||||||
|
|
||||||
let oled_clone = Arc::clone(&oled);
|
let oled_clone = Arc::clone(&oled);
|
||||||
|
|
||||||
io.add_sync_method("flush", move |_| {
|
io.add_method("flush", move |_| {
|
||||||
let mut oled = oled_clone.lock().unwrap();
|
let mut oled = oled_clone.lock().unwrap();
|
||||||
info!("Flushing the display.");
|
info!("Flushing the display.");
|
||||||
oled.flush().unwrap_or_else(|_| {
|
oled.flush().unwrap_or_else(|_| {
|
||||||
@ -134,9 +160,9 @@ pub fn run() -> Result<(), OledError> {
|
|||||||
|
|
||||||
let oled_clone = Arc::clone(&oled);
|
let oled_clone = Arc::clone(&oled);
|
||||||
|
|
||||||
io.add_sync_method("ping", |_| Ok(Value::String("success".to_string())));
|
io.add_method("ping", |_| Ok(Value::String("success".to_string())));
|
||||||
|
|
||||||
io.add_sync_method("power", move |params: Params| {
|
io.add_method("power", move |params: Params| {
|
||||||
let o: Result<On, Error> = params.parse();
|
let o: Result<On, Error> = params.parse();
|
||||||
let o: On = o?;
|
let o: On = o?;
|
||||||
let mut oled = oled_clone.lock().unwrap();
|
let mut oled = oled_clone.lock().unwrap();
|
||||||
@ -154,36 +180,37 @@ pub fn run() -> Result<(), OledError> {
|
|||||||
|
|
||||||
let oled_clone = Arc::clone(&oled);
|
let oled_clone = Arc::clone(&oled);
|
||||||
|
|
||||||
io.add_sync_method("write", move |params: Params| {
|
io.add_method("write", move |params: Params| {
|
||||||
info!("Received a 'write' request.");
|
info!("Received a 'write' request.");
|
||||||
let msg = params.parse()?;
|
let m: Result<Msg, Error> = params.parse();
|
||||||
validate(&msg)?;
|
let m: Msg = m?;
|
||||||
|
validate(&m)?;
|
||||||
|
|
||||||
let mut oled = oled_clone.lock().unwrap();
|
let mut oled = oled_clone.lock().unwrap();
|
||||||
|
|
||||||
info!("Writing to the display.");
|
info!("Writing to the display.");
|
||||||
if msg.font_size == "6x8" {
|
if m.font_size == "6x8" {
|
||||||
oled.draw(
|
oled.draw(
|
||||||
Font6x8::render_str(&msg.string)
|
Font6x8::render_str(&m.string)
|
||||||
.translate(Coord::new(msg.x_coord, msg.y_coord))
|
.translate(Coord::new(m.x_coord, m.y_coord))
|
||||||
.into_iter(),
|
.into_iter(),
|
||||||
);
|
);
|
||||||
} else if msg.font_size == "6x12" {
|
} else if m.font_size == "6x12" {
|
||||||
oled.draw(
|
oled.draw(
|
||||||
Font6x12::render_str(&msg.string)
|
Font6x12::render_str(&m.string)
|
||||||
.translate(Coord::new(msg.x_coord, msg.y_coord))
|
.translate(Coord::new(m.x_coord, m.y_coord))
|
||||||
.into_iter(),
|
.into_iter(),
|
||||||
);
|
);
|
||||||
} else if msg.font_size == "8x16" {
|
} else if m.font_size == "8x16" {
|
||||||
oled.draw(
|
oled.draw(
|
||||||
Font8x16::render_str(&msg.string)
|
Font8x16::render_str(&m.string)
|
||||||
.translate(Coord::new(msg.x_coord, msg.y_coord))
|
.translate(Coord::new(m.x_coord, m.y_coord))
|
||||||
.into_iter(),
|
.into_iter(),
|
||||||
);
|
);
|
||||||
} else if msg.font_size == "12x16" {
|
} else if m.font_size == "12x16" {
|
||||||
oled.draw(
|
oled.draw(
|
||||||
Font12x16::render_str(&msg.string)
|
Font12x16::render_str(&m.string)
|
||||||
.translate(Coord::new(msg.x_coord, msg.y_coord))
|
.translate(Coord::new(m.x_coord, m.y_coord))
|
||||||
.into_iter(),
|
.into_iter(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -228,7 +255,7 @@ mod tests {
|
|||||||
fn rpc_success() {
|
fn rpc_success() {
|
||||||
let rpc = {
|
let rpc = {
|
||||||
let mut io = IoHandler::new();
|
let mut io = IoHandler::new();
|
||||||
io.add_sync_method("rpc_success_response", |_| {
|
io.add_method("rpc_success_response", |_| {
|
||||||
Ok(Value::String("success".into()))
|
Ok(Value::String("success".into()))
|
||||||
});
|
});
|
||||||
test_rpc::Rpc::from(io)
|
test_rpc::Rpc::from(io)
|
||||||
@ -242,7 +269,7 @@ mod tests {
|
|||||||
fn rpc_internal_error() {
|
fn rpc_internal_error() {
|
||||||
let rpc = {
|
let rpc = {
|
||||||
let mut io = IoHandler::new();
|
let mut io = IoHandler::new();
|
||||||
io.add_sync_method("rpc_internal_error", |_| Err(Error::internal_error()));
|
io.add_method("rpc_internal_error", |_| Err(Error::internal_error()));
|
||||||
test_rpc::Rpc::from(io)
|
test_rpc::Rpc::from(io)
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -260,7 +287,7 @@ mod tests {
|
|||||||
fn rpc_i2c_io_error() {
|
fn rpc_i2c_io_error() {
|
||||||
let rpc = {
|
let rpc = {
|
||||||
let mut io = IoHandler::new();
|
let mut io = IoHandler::new();
|
||||||
io.add_sync_method("rpc_i2c_io_error", |_| {
|
io.add_method("rpc_i2c_io_error", |_| {
|
||||||
let io_err = IoError::new(ErrorKind::PermissionDenied, "oh no!");
|
let io_err = IoError::new(ErrorKind::PermissionDenied, "oh no!");
|
||||||
let source = LinuxI2CError::Io(io_err);
|
let source = LinuxI2CError::Io(io_err);
|
||||||
Err(Error::from(OledError::I2CError { source }))
|
Err(Error::from(OledError::I2CError { source }))
|
||||||
@ -283,7 +310,7 @@ mod tests {
|
|||||||
fn rpc_i2c_nix_error() {
|
fn rpc_i2c_nix_error() {
|
||||||
let rpc = {
|
let rpc = {
|
||||||
let mut io = IoHandler::new();
|
let mut io = IoHandler::new();
|
||||||
io.add_sync_method("rpc_i2c_nix_error", |_| {
|
io.add_method("rpc_i2c_nix_error", |_| {
|
||||||
let nix_err = NixError::InvalidPath;
|
let nix_err = NixError::InvalidPath;
|
||||||
let source = LinuxI2CError::Nix(nix_err);
|
let source = LinuxI2CError::Nix(nix_err);
|
||||||
Err(Error::from(OledError::I2CError { source }))
|
Err(Error::from(OledError::I2CError { source }))
|
||||||
@ -299,14 +326,14 @@ mod tests {
|
|||||||
}"#
|
}"#
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// test to ensure correct InvalidCoordinate error response
|
// test to ensure correct InvalidCoordinate error response
|
||||||
#[test]
|
#[test]
|
||||||
fn rpc_invalid_coord() {
|
fn rpc_invalid_coord() {
|
||||||
let rpc = {
|
let rpc = {
|
||||||
let mut io = IoHandler::new();
|
let mut io = IoHandler::new();
|
||||||
io.add_sync_method("rpc_invalid_coord", |_| {
|
io.add_method("rpc_invalid_coord", |_| {
|
||||||
Err(Error::from(OledError::InvalidCoordinate {
|
Err(Error::from(OledError::InvalidCoordinate {
|
||||||
coord: "x".to_string(),
|
coord: "x".to_string(),
|
||||||
range: "0-128".to_string(),
|
range: "0-128".to_string(),
|
||||||
@ -330,7 +357,7 @@ mod tests {
|
|||||||
fn rpc_invalid_fontsize() {
|
fn rpc_invalid_fontsize() {
|
||||||
let rpc = {
|
let rpc = {
|
||||||
let mut io = IoHandler::new();
|
let mut io = IoHandler::new();
|
||||||
io.add_sync_method("rpc_invalid_fontsize", |_| {
|
io.add_method("rpc_invalid_fontsize", |_| {
|
||||||
Err(Error::from(OledError::InvalidFontSize {
|
Err(Error::from(OledError::InvalidFontSize {
|
||||||
font: "24x32".to_string(),
|
font: "24x32".to_string(),
|
||||||
}))
|
}))
|
||||||
@ -352,7 +379,7 @@ mod tests {
|
|||||||
fn rpc_invalid_string() {
|
fn rpc_invalid_string() {
|
||||||
let rpc = {
|
let rpc = {
|
||||||
let mut io = IoHandler::new();
|
let mut io = IoHandler::new();
|
||||||
io.add_sync_method("rpc_invalid_string", |_| {
|
io.add_method("rpc_invalid_string", |_| {
|
||||||
Err(Error::from(OledError::InvalidString { len: 22 }))
|
Err(Error::from(OledError::InvalidString { len: 22 }))
|
||||||
});
|
});
|
||||||
test_rpc::Rpc::from(io)
|
test_rpc::Rpc::from(io)
|
||||||
@ -372,15 +399,15 @@ mod tests {
|
|||||||
fn rpc_invalid_params() {
|
fn rpc_invalid_params() {
|
||||||
let rpc = {
|
let rpc = {
|
||||||
let mut io = IoHandler::new();
|
let mut io = IoHandler::new();
|
||||||
io.add_sync_method("rpc_invalid_params", |_| {
|
io.add_method("rpc_invalid_params", |_| {
|
||||||
let source = Error {
|
let e = Error {
|
||||||
code: ErrorCode::InvalidParams,
|
code: ErrorCode::InvalidParams,
|
||||||
message: String::from("invalid params"),
|
message: String::from("invalid params"),
|
||||||
data: Some(Value::String(
|
data: Some(Value::String(
|
||||||
"Invalid params: invalid type: null, expected struct Msg.".into(),
|
"Invalid params: invalid type: null, expected struct Msg.".into(),
|
||||||
)),
|
)),
|
||||||
};
|
};
|
||||||
Err(Error::from(OledError::MissingParameter { source }))
|
Err(Error::from(OledError::MissingParameter { e }))
|
||||||
});
|
});
|
||||||
test_rpc::Rpc::from(io)
|
test_rpc::Rpc::from(io)
|
||||||
};
|
};
|
||||||
@ -400,13 +427,13 @@ mod tests {
|
|||||||
fn rpc_parse_error() {
|
fn rpc_parse_error() {
|
||||||
let rpc = {
|
let rpc = {
|
||||||
let mut io = IoHandler::new();
|
let mut io = IoHandler::new();
|
||||||
io.add_sync_method("rpc_parse_error", |_| {
|
io.add_method("rpc_parse_error", |_| {
|
||||||
let source = Error {
|
let e = Error {
|
||||||
code: ErrorCode::ParseError,
|
code: ErrorCode::ParseError,
|
||||||
message: String::from("Parse error"),
|
message: String::from("Parse error"),
|
||||||
data: None,
|
data: None,
|
||||||
};
|
};
|
||||||
Err(Error::from(OledError::ParseError { source }))
|
Err(Error::from(OledError::ParseError { e }))
|
||||||
});
|
});
|
||||||
test_rpc::Rpc::from(io)
|
test_rpc::Rpc::from(io)
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "peach-web"
|
name = "peach-web"
|
||||||
version = "0.4.12"
|
version = "0.4.11"
|
||||||
authors = ["Andrew Reid <gnomad@cryptolab.net>"]
|
authors = ["Andrew Reid <gnomad@cryptolab.net>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
description = "peach-web is a web application which provides a web interface for monitoring and interacting with the PeachCloud device. This allows administration of the single-board computer (ie. Raspberry Pi) running PeachCloud, as well as the ssb-server and related plugins."
|
description = "peach-web is a web application which provides a web interface for monitoring and interacting with the PeachCloud device. This allows administration of the single-board computer (ie. Raspberry Pi) running PeachCloud, as well as the ssb-server and related plugins."
|
||||||
@ -38,16 +38,17 @@ maintenance = { status = "actively-developed" }
|
|||||||
env_logger = "0.8"
|
env_logger = "0.8"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
nest = "1.0.0"
|
nest = "1.0.0"
|
||||||
openssl = { version = "0.10", features = ["vendored"] }
|
|
||||||
peach-lib = { path = "../peach-lib" }
|
peach-lib = { path = "../peach-lib" }
|
||||||
percent-encoding = "2.1.0"
|
percent-encoding = "2.1.0"
|
||||||
regex = "1"
|
|
||||||
rocket = { version = "0.5.0-rc.1", features = ["json", "secrets"] }
|
rocket = { version = "0.5.0-rc.1", features = ["json", "secrets"] }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
snafu = "0.6"
|
snafu = "0.6"
|
||||||
tera = { version = "1.12.1", features = ["builtins"] }
|
tera = { version = "1.12.1", features = ["builtins"] }
|
||||||
|
websocket = "0.26"
|
||||||
|
regex = "1"
|
||||||
xdg = "2.2.0"
|
xdg = "2.2.0"
|
||||||
|
openssl = { version = "0.10", features = ["vendored"] }
|
||||||
|
|
||||||
[dependencies.rocket_dyn_templates]
|
[dependencies.rocket_dyn_templates]
|
||||||
version = "0.1.0-rc.1"
|
version = "0.1.0-rc.1"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# peach-web
|
# peach-web
|
||||||
|
|
||||||
[](https://travis-ci.com/peachcloud/peach-web) 
|
[](https://travis-ci.com/peachcloud/peach-web) 
|
||||||
|
|
||||||
## Web Interface for PeachCloud
|
## Web Interface for PeachCloud
|
||||||
|
|
||||||
@ -39,22 +39,12 @@ _Note: Networking functionality requires peach-network microservice to be runnin
|
|||||||
|
|
||||||
### Environment
|
### Environment
|
||||||
|
|
||||||
**Deployment Mode**
|
|
||||||
|
|
||||||
The web application deployment mode is configured with the `ROCKET_ENV` environment variable:
|
The web application deployment mode is configured with the `ROCKET_ENV` environment variable:
|
||||||
|
|
||||||
`export ROCKET_ENV=stage`
|
`export ROCKET_ENV=stage`
|
||||||
|
|
||||||
Other deployment modes are `dev` and `prod`. Read the [Rocket Environment Configurations docs](https://rocket.rs/v0.5-rc/guide/configuration/#environment-variables) for further information.
|
Other deployment modes are `dev` and `prod`. Read the [Rocket Environment Configurations docs](https://rocket.rs/v0.5-rc/guide/configuration/#environment-variables) for further information.
|
||||||
|
|
||||||
**Authentication**
|
|
||||||
|
|
||||||
Authentication is disabled in `development` mode and enabled by default when running the application in `production` mode. It can be disabled by setting the `ROCKET_DISABLE_AUTH` environment variable to `true`:
|
|
||||||
|
|
||||||
`export ROCKET_DISABLE_AUTH=true`
|
|
||||||
|
|
||||||
**Logging**
|
|
||||||
|
|
||||||
Logging is made available with `env_logger`:
|
Logging is made available with `env_logger`:
|
||||||
|
|
||||||
`export RUST_LOG=info`
|
`export RUST_LOG=info`
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
[development]
|
[development]
|
||||||
template_dir = "templates/"
|
template_dir = "templates/"
|
||||||
disable_auth = true
|
|
||||||
|
|
||||||
[production]
|
[production]
|
||||||
template_dir = "templates/"
|
template_dir = "templates/"
|
||||||
disable_auth = false
|
|
||||||
|
@ -1,13 +1,10 @@
|
|||||||
use log::info;
|
use log::info;
|
||||||
use rocket::form::{Form, FromForm};
|
use rocket::form::{Form, FromForm};
|
||||||
use rocket::http::{Cookie, CookieJar, Status};
|
use rocket::request::FlashMessage;
|
||||||
use rocket::request::{self, FlashMessage, FromRequest, Request};
|
|
||||||
use rocket::response::{Flash, Redirect};
|
use rocket::response::{Flash, Redirect};
|
||||||
use rocket::serde::{
|
use rocket::serde::json::Json;
|
||||||
json::{Json, Value},
|
use rocket::serde::{Deserialize, Serialize};
|
||||||
Deserialize, Serialize,
|
use rocket::{get, post};
|
||||||
};
|
|
||||||
use rocket::{get, post, Config};
|
|
||||||
use rocket_dyn_templates::Template;
|
use rocket_dyn_templates::Template;
|
||||||
|
|
||||||
use peach_lib::error::PeachError;
|
use peach_lib::error::PeachError;
|
||||||
@ -15,6 +12,9 @@ use peach_lib::password_utils;
|
|||||||
|
|
||||||
use crate::error::PeachWebError;
|
use crate::error::PeachWebError;
|
||||||
use crate::utils::{build_json_response, TemplateOrRedirect};
|
use crate::utils::{build_json_response, TemplateOrRedirect};
|
||||||
|
use rocket::http::{Cookie, CookieJar, Status};
|
||||||
|
use rocket::request::{self, FromRequest, Request};
|
||||||
|
use rocket::serde::json::Value;
|
||||||
|
|
||||||
// HELPERS AND STRUCTS FOR AUTHENTICATION WITH COOKIES
|
// HELPERS AND STRUCTS FOR AUTHENTICATION WITH COOKIES
|
||||||
|
|
||||||
@ -42,27 +42,14 @@ impl<'r> FromRequest<'r> for Authenticated {
|
|||||||
type Error = LoginError;
|
type Error = LoginError;
|
||||||
|
|
||||||
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
|
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
|
||||||
// check for `disable_auth` config value; set to `false` if unset
|
let authenticated = req
|
||||||
// can be set via the `ROCKET_DISABLE_AUTH` environment variable
|
.cookies()
|
||||||
// - env var, if set, takes precedence over value defined in `Rocket.toml`
|
.get_private(AUTH_COOKIE_KEY)
|
||||||
let authentication_is_disabled: bool = match Config::figment().find_value("disable_auth") {
|
.and_then(|cookie| cookie.value().parse().ok())
|
||||||
// deserialize the boolean value; set to `false` if an error is encountered
|
.map(|_value: String| Authenticated {});
|
||||||
Ok(value) => value.deserialize().unwrap_or(false),
|
match authenticated {
|
||||||
Err(_) => false,
|
Some(auth) => request::Outcome::Success(auth),
|
||||||
};
|
None => request::Outcome::Failure((Status::Forbidden, LoginError::UserNotLoggedIn)),
|
||||||
if authentication_is_disabled {
|
|
||||||
let auth = Authenticated {};
|
|
||||||
request::Outcome::Success(auth)
|
|
||||||
} else {
|
|
||||||
let authenticated = req
|
|
||||||
.cookies()
|
|
||||||
.get_private(AUTH_COOKIE_KEY)
|
|
||||||
.and_then(|cookie| cookie.value().parse().ok())
|
|
||||||
.map(|_value: String| Authenticated {});
|
|
||||||
match authenticated {
|
|
||||||
Some(auth) => request::Outcome::Success(auth),
|
|
||||||
None => request::Outcome::Failure((Status::Forbidden, LoginError::UserNotLoggedIn)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,29 +4,17 @@ use std::io::Read;
|
|||||||
use rocket::http::{ContentType, Status};
|
use rocket::http::{ContentType, Status};
|
||||||
use rocket::local::blocking::Client;
|
use rocket::local::blocking::Client;
|
||||||
use rocket::serde::json::{json, Value};
|
use rocket::serde::json::{json, Value};
|
||||||
use rocket::{Build, Config, Rocket};
|
|
||||||
|
|
||||||
use crate::utils::build_json_response;
|
use crate::utils::build_json_response;
|
||||||
|
|
||||||
use super::init_rocket;
|
use super::init_rocket;
|
||||||
|
|
||||||
// define authentication mode
|
|
||||||
const DISABLE_AUTH: bool = true;
|
|
||||||
|
|
||||||
/// Wrapper around `init_rocket()` to simplify the process of invoking the application with the desired authentication status. This is particularly useful for testing purposes.
|
|
||||||
fn init_test_rocket(disable_auth: bool) -> Rocket<Build> {
|
|
||||||
// set authentication based on provided `disable_auth` value
|
|
||||||
Config::figment().merge(("disable_auth", disable_auth));
|
|
||||||
|
|
||||||
init_rocket()
|
|
||||||
}
|
|
||||||
|
|
||||||
// helper function to test correct retrieval and content of a file
|
// helper function to test correct retrieval and content of a file
|
||||||
fn test_query_file<T>(path: &str, file: T, status: Status)
|
fn test_query_file<T>(path: &str, file: T, status: Status)
|
||||||
where
|
where
|
||||||
T: Into<Option<&'static str>>,
|
T: Into<Option<&'static str>>,
|
||||||
{
|
{
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).unwrap();
|
let client = Client::tracked(init_rocket()).unwrap();
|
||||||
let response = client.get(path).dispatch();
|
let response = client.get(path).dispatch();
|
||||||
assert_eq!(response.status(), status);
|
assert_eq!(response.status(), status);
|
||||||
|
|
||||||
@ -51,7 +39,7 @@ fn read_file_content(path: &str) -> Vec<u8> {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn index_html() {
|
fn index_html() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client.get("/").dispatch();
|
let response = client.get("/").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
@ -66,7 +54,7 @@ fn index_html() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn help_html() {
|
fn help_html() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client.get("/help").dispatch();
|
let response = client.get("/help").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
@ -76,7 +64,7 @@ fn help_html() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn login_html() {
|
fn login_html() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client.get("/login").dispatch();
|
let response = client.get("/login").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
@ -86,7 +74,7 @@ fn login_html() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn logout_html() {
|
fn logout_html() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client.get("/logout").dispatch();
|
let response = client.get("/logout").dispatch();
|
||||||
// check for 303 status (redirect to "/login")
|
// check for 303 status (redirect to "/login")
|
||||||
assert_eq!(response.status(), Status::SeeOther);
|
assert_eq!(response.status(), Status::SeeOther);
|
||||||
@ -95,7 +83,7 @@ fn logout_html() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn power_html() {
|
fn power_html() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client.get("/power").dispatch();
|
let response = client.get("/power").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
@ -109,7 +97,7 @@ NOTE: these tests are comment-out for the moment, due to the fact that they invo
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn reboot() {
|
fn reboot() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client.get("/power/reboot").dispatch();
|
let response = client.get("/power/reboot").dispatch();
|
||||||
// check for redirect
|
// check for redirect
|
||||||
assert_eq!(response.status(), Status::SeeOther);
|
assert_eq!(response.status(), Status::SeeOther);
|
||||||
@ -117,7 +105,7 @@ fn reboot() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn shutdown() {
|
fn shutdown() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client.get("/power/shutdown").dispatch();
|
let response = client.get("/power/shutdown").dispatch();
|
||||||
// check for redirect
|
// check for redirect
|
||||||
assert_eq!(response.status(), Status::SeeOther);
|
assert_eq!(response.status(), Status::SeeOther);
|
||||||
@ -128,7 +116,7 @@ fn shutdown() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn block() {
|
fn block() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client
|
let response = client
|
||||||
.post("/scuttlebutt/block")
|
.post("/scuttlebutt/block")
|
||||||
.header(ContentType::Form)
|
.header(ContentType::Form)
|
||||||
@ -139,7 +127,7 @@ fn block() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn blocks_html() {
|
fn blocks_html() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client.get("/scuttlebutt/blocks").dispatch();
|
let response = client.get("/scuttlebutt/blocks").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
@ -149,7 +137,7 @@ fn blocks_html() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn follow() {
|
fn follow() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client
|
let response = client
|
||||||
.post("/scuttlebutt/follow")
|
.post("/scuttlebutt/follow")
|
||||||
.header(ContentType::Form)
|
.header(ContentType::Form)
|
||||||
@ -161,7 +149,7 @@ fn follow() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn follows_html() {
|
fn follows_html() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client.get("/scuttlebutt/follows").dispatch();
|
let response = client.get("/scuttlebutt/follows").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
@ -171,7 +159,7 @@ fn follows_html() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn followers_html() {
|
fn followers_html() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client.get("/scuttlebutt/followers").dispatch();
|
let response = client.get("/scuttlebutt/followers").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
@ -181,7 +169,7 @@ fn followers_html() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn friends_html() {
|
fn friends_html() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client.get("/scuttlebutt/friends").dispatch();
|
let response = client.get("/scuttlebutt/friends").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
@ -191,7 +179,7 @@ fn friends_html() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn peers_html() {
|
fn peers_html() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client.get("/scuttlebutt/peers").dispatch();
|
let response = client.get("/scuttlebutt/peers").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
@ -201,7 +189,7 @@ fn peers_html() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn private_html() {
|
fn private_html() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client.get("/scuttlebutt/private").dispatch();
|
let response = client.get("/scuttlebutt/private").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
@ -211,7 +199,7 @@ fn private_html() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn profile_html() {
|
fn profile_html() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client.get("/scuttlebutt/profile").dispatch();
|
let response = client.get("/scuttlebutt/profile").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
@ -221,7 +209,7 @@ fn profile_html() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn publish_post() {
|
fn publish_post() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client
|
let response = client
|
||||||
.post("/scuttlebutt/publish")
|
.post("/scuttlebutt/publish")
|
||||||
.header(ContentType::Form)
|
.header(ContentType::Form)
|
||||||
@ -232,7 +220,7 @@ fn publish_post() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn unfollow() {
|
fn unfollow() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client
|
let response = client
|
||||||
.post("/scuttlebutt/unfollow")
|
.post("/scuttlebutt/unfollow")
|
||||||
.header(ContentType::Form)
|
.header(ContentType::Form)
|
||||||
@ -245,7 +233,7 @@ fn unfollow() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn admin_settings_menu_html() {
|
fn admin_settings_menu_html() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client.get("/settings/admin").dispatch();
|
let response = client.get("/settings/admin").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
@ -257,7 +245,7 @@ fn admin_settings_menu_html() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add_admin_html() {
|
fn add_admin_html() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client.get("/settings/admin/add").dispatch();
|
let response = client.get("/settings/admin/add").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
@ -270,7 +258,7 @@ fn add_admin_html() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add_admin() {
|
fn add_admin() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client
|
let response = client
|
||||||
.post("/settings/admin/add")
|
.post("/settings/admin/add")
|
||||||
.header(ContentType::Form)
|
.header(ContentType::Form)
|
||||||
@ -282,21 +270,21 @@ fn add_admin() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn change_password_html() {
|
fn change_password_html() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client.get("/settings/admin/change_password").dispatch();
|
let response = client.get("/settings/admin/change_password").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
let body = response.into_string().unwrap();
|
let body = response.into_string().unwrap();
|
||||||
assert!(body.contains("Change Password"));
|
assert!(body.contains("Change Password"));
|
||||||
assert!(body.contains("Current password"));
|
assert!(body.contains("Old Password"));
|
||||||
assert!(body.contains("New password"));
|
assert!(body.contains("Enter New Password"));
|
||||||
assert!(body.contains("New password duplicate"));
|
assert!(body.contains("Re-Enter New Password"));
|
||||||
assert!(body.contains("Save"));
|
assert!(body.contains("Save"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn configure_admin_html() {
|
fn configure_admin_html() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client.get("/settings/admin/configure").dispatch();
|
let response = client.get("/settings/admin/configure").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
@ -308,7 +296,7 @@ fn configure_admin_html() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn forgot_password_html() {
|
fn forgot_password_html() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client.get("/settings/admin/forgot_password").dispatch();
|
let response = client.get("/settings/admin/forgot_password").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
@ -320,7 +308,7 @@ fn forgot_password_html() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn network_settings_menu_html() {
|
fn network_settings_menu_html() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client.get("/settings/network").dispatch();
|
let response = client.get("/settings/network").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
@ -330,7 +318,7 @@ fn network_settings_menu_html() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn deploy_ap() {
|
fn deploy_ap() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client.get("/settings/network/ap/activate").dispatch();
|
let response = client.get("/settings/network/ap/activate").dispatch();
|
||||||
// check for 303 status (redirect)
|
// check for 303 status (redirect)
|
||||||
assert_eq!(response.status(), Status::SeeOther);
|
assert_eq!(response.status(), Status::SeeOther);
|
||||||
@ -339,7 +327,7 @@ fn deploy_ap() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn dns_settings_html() {
|
fn dns_settings_html() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client.get("/settings/network/dns").dispatch();
|
let response = client.get("/settings/network/dns").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
@ -353,7 +341,7 @@ fn dns_settings_html() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn list_aps_html() {
|
fn list_aps_html() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client.get("/settings/network/wifi").dispatch();
|
let response = client.get("/settings/network/wifi").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
@ -365,7 +353,7 @@ fn list_aps_html() {
|
|||||||
// TODO: needs further testing once template has been refactored
|
// TODO: needs further testing once template has been refactored
|
||||||
#[test]
|
#[test]
|
||||||
fn ap_details_html() {
|
fn ap_details_html() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client.get("/settings/network/wifi?ssid=Home").dispatch();
|
let response = client.get("/settings/network/wifi?ssid=Home").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
@ -375,7 +363,7 @@ fn ap_details_html() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn deploy_client() {
|
fn deploy_client() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client.get("/settings/network/wifi/activate").dispatch();
|
let response = client.get("/settings/network/wifi/activate").dispatch();
|
||||||
// check for 303 status (redirect)
|
// check for 303 status (redirect)
|
||||||
assert_eq!(response.status(), Status::SeeOther);
|
assert_eq!(response.status(), Status::SeeOther);
|
||||||
@ -384,7 +372,7 @@ fn deploy_client() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add_ap_html() {
|
fn add_ap_html() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client.get("/settings/network/wifi/add").dispatch();
|
let response = client.get("/settings/network/wifi/add").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
@ -398,7 +386,7 @@ fn add_ap_html() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add_ap_ssid_html() {
|
fn add_ap_ssid_html() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client
|
let response = client
|
||||||
.get("/settings/network/wifi/add?ssid=Home")
|
.get("/settings/network/wifi/add?ssid=Home")
|
||||||
.dispatch();
|
.dispatch();
|
||||||
@ -414,7 +402,7 @@ fn add_ap_ssid_html() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add_credentials() {
|
fn add_credentials() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client
|
let response = client
|
||||||
.post("/settings/network/wifi/add")
|
.post("/settings/network/wifi/add")
|
||||||
.header(ContentType::Form)
|
.header(ContentType::Form)
|
||||||
@ -426,7 +414,7 @@ fn add_credentials() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn forget_wifi() {
|
fn forget_wifi() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client
|
let response = client
|
||||||
.post("/settings/network/wifi/forget")
|
.post("/settings/network/wifi/forget")
|
||||||
.header(ContentType::Form)
|
.header(ContentType::Form)
|
||||||
@ -438,7 +426,7 @@ fn forget_wifi() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn modify_password() {
|
fn modify_password() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client
|
let response = client
|
||||||
.post("/settings/network/wifi/modify")
|
.post("/settings/network/wifi/modify")
|
||||||
.header(ContentType::Form)
|
.header(ContentType::Form)
|
||||||
@ -450,7 +438,7 @@ fn modify_password() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn data_usage_html() {
|
fn data_usage_html() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client.get("/settings/network/wifi/usage").dispatch();
|
let response = client.get("/settings/network/wifi/usage").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
@ -465,7 +453,7 @@ fn data_usage_html() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn scuttlebutt_settings_menu_html() {
|
fn scuttlebutt_settings_menu_html() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client.get("/settings/scuttlebutt").dispatch();
|
let response = client.get("/settings/scuttlebutt").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
@ -484,7 +472,7 @@ fn scuttlebutt_settings_menu_html() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn status_html() {
|
fn status_html() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client.get("/status").dispatch();
|
let response = client.get("/status").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
@ -497,7 +485,7 @@ fn status_html() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn network_status_html() {
|
fn network_status_html() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client.get("/status/network").dispatch();
|
let response = client.get("/status/network").dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
assert_eq!(response.content_type(), Some(ContentType::HTML));
|
||||||
@ -514,7 +502,7 @@ fn network_status_html() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn activate_ap() {
|
fn activate_ap() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client
|
let response = client
|
||||||
.post("/api/v1/network/activate_ap")
|
.post("/api/v1/network/activate_ap")
|
||||||
.header(ContentType::JSON)
|
.header(ContentType::JSON)
|
||||||
@ -525,7 +513,7 @@ fn activate_ap() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn activate_client() {
|
fn activate_client() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client
|
let response = client
|
||||||
.post("/api/v1/network/activate_client")
|
.post("/api/v1/network/activate_client")
|
||||||
.header(ContentType::JSON)
|
.header(ContentType::JSON)
|
||||||
@ -536,7 +524,7 @@ fn activate_client() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn return_ip() {
|
fn return_ip() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client
|
let response = client
|
||||||
.get("/api/v1/network/ip")
|
.get("/api/v1/network/ip")
|
||||||
.header(ContentType::JSON)
|
.header(ContentType::JSON)
|
||||||
@ -550,7 +538,7 @@ fn return_ip() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn return_rssi() {
|
fn return_rssi() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client
|
let response = client
|
||||||
.get("/api/v1/network/rssi")
|
.get("/api/v1/network/rssi")
|
||||||
.header(ContentType::JSON)
|
.header(ContentType::JSON)
|
||||||
@ -563,7 +551,7 @@ fn return_rssi() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn return_ssid() {
|
fn return_ssid() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client
|
let response = client
|
||||||
.get("/api/v1/network/ssid")
|
.get("/api/v1/network/ssid")
|
||||||
.header(ContentType::JSON)
|
.header(ContentType::JSON)
|
||||||
@ -576,7 +564,7 @@ fn return_ssid() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn return_state() {
|
fn return_state() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client
|
let response = client
|
||||||
.get("/api/v1/network/state")
|
.get("/api/v1/network/state")
|
||||||
.header(ContentType::JSON)
|
.header(ContentType::JSON)
|
||||||
@ -591,7 +579,7 @@ fn return_state() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn return_status() {
|
fn return_status() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client
|
let response = client
|
||||||
.get("/api/v1/network/status")
|
.get("/api/v1/network/status")
|
||||||
.header(ContentType::JSON)
|
.header(ContentType::JSON)
|
||||||
@ -604,7 +592,7 @@ fn return_status() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn scan_networks() {
|
fn scan_networks() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client
|
let response = client
|
||||||
.get("/api/v1/network/wifi")
|
.get("/api/v1/network/wifi")
|
||||||
.header(ContentType::JSON)
|
.header(ContentType::JSON)
|
||||||
@ -617,7 +605,7 @@ fn scan_networks() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add_wifi() {
|
fn add_wifi() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client
|
let response = client
|
||||||
.post("/api/v1/network/wifi")
|
.post("/api/v1/network/wifi")
|
||||||
.header(ContentType::JSON)
|
.header(ContentType::JSON)
|
||||||
@ -631,7 +619,7 @@ fn add_wifi() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn remove_wifi() {
|
fn remove_wifi() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client
|
let response = client
|
||||||
.post("/api/v1/network/wifi/forget")
|
.post("/api/v1/network/wifi/forget")
|
||||||
.header(ContentType::JSON)
|
.header(ContentType::JSON)
|
||||||
@ -645,7 +633,7 @@ fn remove_wifi() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn new_password() {
|
fn new_password() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client
|
let response = client
|
||||||
.post("/api/v1/network/wifi/modify")
|
.post("/api/v1/network/wifi/modify")
|
||||||
.header(ContentType::JSON)
|
.header(ContentType::JSON)
|
||||||
@ -659,7 +647,7 @@ fn new_password() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ping_pong() {
|
fn ping_pong() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance");
|
let client = Client::tracked(init_rocket()).expect("valid rocket instance");
|
||||||
let response = client
|
let response = client
|
||||||
.get("/api/v1/ping")
|
.get("/api/v1/ping")
|
||||||
.header(ContentType::JSON)
|
.header(ContentType::JSON)
|
||||||
@ -721,7 +709,7 @@ fn invalid_path() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn invalid_get_request() {
|
fn invalid_get_request() {
|
||||||
let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).unwrap();
|
let client = Client::tracked(init_rocket()).unwrap();
|
||||||
|
|
||||||
// try to get a path that doesn't exist
|
// try to get a path that doesn't exist
|
||||||
let res = client
|
let res = client
|
||||||
|
@ -220,18 +220,12 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.capsule-container {
|
.capsule-container {
|
||||||
margin-left: 1rem;
|
margin-left: 2rem;
|
||||||
margin-right: 1rem;
|
margin-right: 2rem;
|
||||||
|
padding-top: 1rem;
|
||||||
padding-bottom: 1rem;
|
padding-bottom: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media only screen and (min-width: 600px) {
|
|
||||||
.capsule-container {
|
|
||||||
margin-left: 0;
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* CARDS
|
* CARDS
|
||||||
*/
|
*/
|
||||||
@ -241,7 +235,6 @@ body {
|
|||||||
max-height: 90vh;
|
max-height: 90vh;
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-top: 1rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media only screen and (min-width: 600px) {
|
@media only screen and (min-width: 600px) {
|
||||||
@ -255,6 +248,8 @@ body {
|
|||||||
.card-container {
|
.card-container {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
|
/* padding-top: 1rem; */
|
||||||
|
/* padding-bottom: 1rem; */
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-container {
|
.form-container {
|
||||||
@ -565,7 +560,6 @@ html {
|
|||||||
font-size: var(--font-size-6);
|
font-size: var(--font-size-6);
|
||||||
margin-left: 2rem;
|
margin-left: 2rem;
|
||||||
margin-right: 2rem;
|
margin-right: 2rem;
|
||||||
margin-top: 1rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1,28 +1,16 @@
|
|||||||
/*
|
/*
|
||||||
|
* behavioural layer for the `change_password.html.tera` template,
|
||||||
behavioural layer for the `change_password.html.tera` template
|
*/
|
||||||
|
|
||||||
- intercept button click for save (form submission of passwords)
|
|
||||||
- perform json api call
|
|
||||||
- update the dom
|
|
||||||
|
|
||||||
methods:
|
|
||||||
|
|
||||||
PEACH_AUTH.changePassword();
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
var PEACH_AUTH = {};
|
|
||||||
|
|
||||||
// catch click of 'Save' button and make POST request
|
// catch click of 'Save' button and make POST request
|
||||||
PEACH_AUTH.changePassword = function() {
|
PEACH.add = function() {
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
document.body.addEventListener('submit', function(e) {
|
document.body.addEventListener('submit', function(e) {
|
||||||
// prevent redirect on button press (default behavior)
|
// prevent redirect on button press (default behavior)
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
// capture form data
|
// capture form data
|
||||||
var formElement = document.querySelector("form");
|
var formElement = document.querySelector("form");
|
||||||
// create form data object from the changePassword form element
|
// create form data object from the wifiCreds form element
|
||||||
var formData = new FormData(formElement);
|
var formData = new FormData(formElement);
|
||||||
var object = {};
|
var object = {};
|
||||||
// assign values from form
|
// assign values from form
|
||||||
@ -34,7 +22,7 @@ PEACH_AUTH.changePassword = function() {
|
|||||||
var jsonData = JSON.stringify(object);
|
var jsonData = JSON.stringify(object);
|
||||||
// write in-progress status message to ui
|
// write in-progress status message to ui
|
||||||
PEACH.flashMsg("info", "Saving new password.");
|
PEACH.flashMsg("info", "Saving new password.");
|
||||||
// send change_password POST request
|
// send add_wifi POST request
|
||||||
fetch("/api/v1/admin/change_password", {
|
fetch("/api/v1/admin/change_password", {
|
||||||
method: "post",
|
method: "post",
|
||||||
headers: {
|
headers: {
|
||||||
@ -53,5 +41,5 @@ PEACH_AUTH.changePassword = function() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var changePassInstance = PEACH_AUTH;
|
var addInstance = PEACH;
|
||||||
changePassInstance.changePassword();
|
addInstance.add();
|
||||||
|
@ -43,4 +43,5 @@ PEACH.flashMsg = function(status, msg) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var commonInstance = PEACH;
|
var addInstance = PEACH;
|
||||||
|
addInstance.add();
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
/*
|
/*
|
||||||
|
|
||||||
behavioural layer for the `configure_dns.html.tera` template,
|
behavioural layer for the `configure_dns.html.tera` template,
|
||||||
corresponding to the web route `/settings/network/dns`
|
corresponding to the web route `/network/dns`
|
||||||
|
|
||||||
- intercept button click for save (form submission of dns settings)
|
- intercept button click for add (form submission of credentials)
|
||||||
- perform json api call
|
- perform json api call
|
||||||
- update the dom
|
- update the dom
|
||||||
|
|
||||||
@ -12,14 +12,14 @@ corresponding to the web route `/settings/network/dns`
|
|||||||
var PEACH_DNS = {};
|
var PEACH_DNS = {};
|
||||||
|
|
||||||
// catch click of 'Add' button and make POST request
|
// catch click of 'Add' button and make POST request
|
||||||
PEACH_DNS.configureDns = function() {
|
PEACH_DNS.add = function() {
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
document.body.addEventListener('submit', function(e) {
|
document.body.addEventListener('submit', function(e) {
|
||||||
// prevent redirect on button press (default behavior)
|
// prevent redirect on button press (default behavior)
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
// capture form data
|
// capture form data
|
||||||
var formElement = document.querySelector("form");
|
var formElement = document.querySelector("form");
|
||||||
// create form data object from the configureDNS form element
|
// create form data object from the wifiCreds form element
|
||||||
var formData = new FormData(formElement);
|
var formData = new FormData(formElement);
|
||||||
var object = {};
|
var object = {};
|
||||||
// set checkbox to false (the value is only passed to formData if it is "on")
|
// set checkbox to false (the value is only passed to formData if it is "on")
|
||||||
@ -36,7 +36,7 @@ PEACH_DNS.configureDns = function() {
|
|||||||
console.log(object);
|
console.log(object);
|
||||||
var jsonData = JSON.stringify(object);
|
var jsonData = JSON.stringify(object);
|
||||||
// write in-progress status message to ui
|
// write in-progress status message to ui
|
||||||
PEACH.flashMsg("info", "Saving new DNS configurations");
|
PEACH_DNS.flashMsg("info", "Saving new DNS configurations");
|
||||||
// send add_wifi POST request
|
// send add_wifi POST request
|
||||||
fetch("/api/v1/network/dns/configure", {
|
fetch("/api/v1/network/dns/configure", {
|
||||||
method: "post",
|
method: "post",
|
||||||
@ -50,14 +50,49 @@ PEACH_DNS.configureDns = function() {
|
|||||||
})
|
})
|
||||||
.then( (jsonData) => {
|
.then( (jsonData) => {
|
||||||
// write json response message to ui
|
// write json response message to ui
|
||||||
PEACH.flashMsg(jsonData.status, jsonData.msg);
|
PEACH_DNS.flashMsg(jsonData.status, jsonData.msg);
|
||||||
let statusIndicator = document.getElementById("dyndns-status-indicator");
|
let statusIndicator = document.getElementById("dyndns-status-indicator");
|
||||||
// only remove the "dyndns-status-indicator" element if it exists
|
statusIndicator.remove();
|
||||||
if (statusIndicator != null ) statusIndicator.remove();
|
|
||||||
})
|
})
|
||||||
}, false);
|
}, false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var configureDnsInstance = PEACH_DNS;
|
// display a message by appending a paragraph element
|
||||||
configureDnsInstance.configureDns();
|
PEACH_DNS.flashMsg = function(status, msg) {
|
||||||
|
// set the class of the element according to status
|
||||||
|
var elementClass;
|
||||||
|
if (status === "success") {
|
||||||
|
elementClass = "capsule center-text flash-message font-success";
|
||||||
|
} else if (status === "info") {
|
||||||
|
elementClass = "capsule center-text flash-message font-info";
|
||||||
|
} else {
|
||||||
|
elementClass = "capsule center-text flash-message font-failure";
|
||||||
|
};
|
||||||
|
|
||||||
|
var flashElement = document.getElementById("flashMsg");
|
||||||
|
// if flashElement exists, update the class & text
|
||||||
|
if (flashElement) {
|
||||||
|
flashElement.className = elementClass;
|
||||||
|
flashElement.innerText = msg;
|
||||||
|
// if flashElement does not exist, create it, set id, class, text & append
|
||||||
|
} else {
|
||||||
|
// create new div for flash message
|
||||||
|
var flashDiv = document.createElement("DIV");
|
||||||
|
// set div attributes
|
||||||
|
flashDiv.id = "flashMsg";
|
||||||
|
flashDiv.className = elementClass;
|
||||||
|
// add json response message to flash message div
|
||||||
|
var flashMsg = document.createTextNode(msg);
|
||||||
|
flashDiv.appendChild(flashMsg);
|
||||||
|
// insert the flash message div below the button div
|
||||||
|
var buttonDiv = document.getElementById("buttonDiv");
|
||||||
|
// flashDiv will be added to the end since buttonDiv is the last
|
||||||
|
// child within the parent element (card-container div)
|
||||||
|
buttonDiv.parentNode.insertBefore(flashDiv, buttonDiv.nextSibling);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var addInstance = PEACH_DNS;
|
||||||
|
addInstance.add();
|
||||||
|
@ -10,6 +10,7 @@ corresponding to the web route `/network/wifi/add`
|
|||||||
methods:
|
methods:
|
||||||
|
|
||||||
PEACH_NETWORK.add();
|
PEACH_NETWORK.add();
|
||||||
|
PEACH_NETWORK.flashMsg(status, msg);
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -33,7 +34,7 @@ PEACH_NETWORK.add = function() {
|
|||||||
// perform json serialization
|
// perform json serialization
|
||||||
var jsonData = JSON.stringify(object);
|
var jsonData = JSON.stringify(object);
|
||||||
// write in-progress status message to ui
|
// write in-progress status message to ui
|
||||||
PEACH.flashMsg("info", "Adding WiFi credentials...");
|
PEACH_NETWORK.flashMsg("info", "Adding WiFi credentials...");
|
||||||
// send add_wifi POST request
|
// send add_wifi POST request
|
||||||
fetch("/api/v1/network/wifi", {
|
fetch("/api/v1/network/wifi", {
|
||||||
method: "post",
|
method: "post",
|
||||||
@ -47,11 +48,46 @@ PEACH_NETWORK.add = function() {
|
|||||||
})
|
})
|
||||||
.then( (jsonData) => {
|
.then( (jsonData) => {
|
||||||
// write json response message to ui
|
// write json response message to ui
|
||||||
PEACH.flashMsg(jsonData.status, jsonData.msg);
|
PEACH_NETWORK.flashMsg(jsonData.status, jsonData.msg);
|
||||||
})
|
})
|
||||||
}, false);
|
}, false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// display a message by appending a paragraph element
|
||||||
|
PEACH_NETWORK.flashMsg = function(status, msg) {
|
||||||
|
// set the class of the element according to status
|
||||||
|
var elementClass;
|
||||||
|
if (status === "success") {
|
||||||
|
elementClass = "capsule center-text flash-message font-success";
|
||||||
|
} else if (status === "info") {
|
||||||
|
elementClass = "capsule center-text flash-message font-info";
|
||||||
|
} else {
|
||||||
|
elementClass = "capsule center-text flash-message font-failure";
|
||||||
|
};
|
||||||
|
|
||||||
|
var flashElement = document.getElementById("flashMsg");
|
||||||
|
// if flashElement exists, update the class & text
|
||||||
|
if (flashElement) {
|
||||||
|
flashElement.className = elementClass;
|
||||||
|
flashElement.innerText = msg;
|
||||||
|
// if flashElement does not exist, create it, set id, class, text & append
|
||||||
|
} else {
|
||||||
|
// create new div for flash message
|
||||||
|
var flashDiv = document.createElement("DIV");
|
||||||
|
// set div attributes
|
||||||
|
flashDiv.id = "flashMsg";
|
||||||
|
flashDiv.className = elementClass;
|
||||||
|
// add json response message to flash message div
|
||||||
|
var flashMsg = document.createTextNode(msg);
|
||||||
|
flashDiv.appendChild(flashMsg);
|
||||||
|
// insert the flash message div below the button div
|
||||||
|
var buttonDiv = document.getElementById("buttonDiv");
|
||||||
|
// flashDiv will be added to the end since buttonDiv is the last
|
||||||
|
// child within the parent element (card-container div)
|
||||||
|
buttonDiv.parentNode.insertBefore(flashDiv, buttonDiv.nextSibling);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var addInstance = PEACH_NETWORK;
|
var addInstance = PEACH_NETWORK;
|
||||||
addInstance.add();
|
addInstance.add();
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
|
|
||||||
behavioural layer for the `network_card.html.tera` template,
|
behavioural layer for the `network_card.html.tera` template,
|
||||||
corresponding to the web route `/settings/network`
|
corresponding to the web route `/network`
|
||||||
|
|
||||||
- intercept form submissions
|
- intercept form submissions
|
||||||
- perform json api calls
|
- perform json api calls
|
||||||
@ -11,8 +11,10 @@ methods:
|
|||||||
|
|
||||||
PEACH_NETWORK.activateAp();
|
PEACH_NETWORK.activateAp();
|
||||||
PEACH_NETWORK.activateClient();
|
PEACH_NETWORK.activateClient();
|
||||||
PEACH_NETWORK.apMode();
|
PEACH_NETWORK.apOnline();
|
||||||
PEACH_NETWORK.clientMode();
|
PEACH_NETWORK.clientOffline();
|
||||||
|
PEACH_NETWORK.clientOnline();
|
||||||
|
PEACH_NETWORK.flashMsg(status, msg);
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -27,7 +29,7 @@ PEACH_NETWORK.activateAp = function() {
|
|||||||
// prevent form submission (default behavior)
|
// prevent form submission (default behavior)
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
// write in-progress status message to ui
|
// write in-progress status message to ui
|
||||||
PEACH.flashMsg("info", "Deploying access point...");
|
PEACH_NETWORK.flashMsg("info", "Deploying access point...");
|
||||||
// send activate_ap POST request
|
// send activate_ap POST request
|
||||||
fetch("/api/v1/network/activate_ap", {
|
fetch("/api/v1/network/activate_ap", {
|
||||||
method: "post",
|
method: "post",
|
||||||
@ -42,10 +44,10 @@ PEACH_NETWORK.activateAp = function() {
|
|||||||
.then( (jsonData) => {
|
.then( (jsonData) => {
|
||||||
console.log(jsonData.msg);
|
console.log(jsonData.msg);
|
||||||
// write json response message to ui
|
// write json response message to ui
|
||||||
PEACH.flashMsg(jsonData.status, jsonData.msg);
|
PEACH_NETWORK.flashMsg(jsonData.status, jsonData.msg);
|
||||||
// if ap activation is successful, update the ui
|
// if ap activation is successful, update the ui
|
||||||
if (jsonData.status === "success") {
|
if (jsonData.status === "success") {
|
||||||
PEACH_NETWORK.apMode();
|
PEACH_NETWORK.apOnline();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, false);
|
}, false);
|
||||||
@ -62,7 +64,7 @@ PEACH_NETWORK.activateClient = function() {
|
|||||||
// prevent form submission (default behavior)
|
// prevent form submission (default behavior)
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
// write in-progress status message to ui
|
// write in-progress status message to ui
|
||||||
PEACH.flashMsg("info", "Enabling WiFi client...");
|
PEACH_NETWORK.flashMsg("info", "Enabling WiFi client...");
|
||||||
// send activate_ap POST request
|
// send activate_ap POST request
|
||||||
fetch("/api/v1/network/activate_client", {
|
fetch("/api/v1/network/activate_client", {
|
||||||
method: "post",
|
method: "post",
|
||||||
@ -77,10 +79,10 @@ PEACH_NETWORK.activateClient = function() {
|
|||||||
.then( (jsonData) => {
|
.then( (jsonData) => {
|
||||||
console.log(jsonData.msg);
|
console.log(jsonData.msg);
|
||||||
// write json response message to ui
|
// write json response message to ui
|
||||||
PEACH.flashMsg(jsonData.status, jsonData.msg);
|
PEACH_NETWORK.flashMsg(jsonData.status, jsonData.msg);
|
||||||
// if client activation is successful, update the ui
|
// if client activation is successful, update the ui
|
||||||
if (jsonData.status === "success") {
|
if (jsonData.status === "success") {
|
||||||
PEACH_NETWORK.clientMode();
|
PEACH_NETWORK.clientOnline();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, false);
|
}, false);
|
||||||
@ -88,12 +90,21 @@ PEACH_NETWORK.activateClient = function() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// replace 'Deploy Access Point' button with 'Enable WiFi' button
|
// update ui for access point mode (status: online)
|
||||||
PEACH_NETWORK.apMode = function() {
|
PEACH_NETWORK.apOnline = function() {
|
||||||
|
console.log('Activating AP Mode');
|
||||||
|
|
||||||
|
// update network mode and status (icon & label)
|
||||||
|
let i = document.getElementById("netModeIcon");
|
||||||
|
i.className = "center icon icon-active";
|
||||||
|
i.src = "icons/router.svg";
|
||||||
|
let l = document.getElementById("netModeLabel");
|
||||||
|
l.textContent = "ONLINE";
|
||||||
|
|
||||||
// create Enable WiFi button and add it to button div
|
// create Enable WiFi button and add it to button div
|
||||||
var wifiButton = document.createElement("A");
|
var wifiButton = document.createElement("A");
|
||||||
wifiButton.className = "button center";
|
wifiButton.className = "button center";
|
||||||
wifiButton.href = "/settings/network/wifi/activate";
|
wifiButton.href = "/network/wifi/activate";
|
||||||
wifiButton.id = "connectWifi";
|
wifiButton.id = "connectWifi";
|
||||||
var label = "Enable WiFi";
|
var label = "Enable WiFi";
|
||||||
var buttonText = document.createTextNode(label);
|
var buttonText = document.createTextNode(label);
|
||||||
@ -103,31 +114,88 @@ PEACH_NETWORK.apMode = function() {
|
|||||||
let buttons = document.getElementById("buttons");
|
let buttons = document.getElementById("buttons");
|
||||||
buttons.appendChild(wifiButton);
|
buttons.appendChild(wifiButton);
|
||||||
|
|
||||||
// remove the old 'Deploy Access Point' button
|
// remove the old 'Activate Access Point' button
|
||||||
let apButton = document.getElementById("deployAccessPoint");
|
let apButton = document.getElementById("deployAccessPoint");
|
||||||
apButton.remove();
|
apButton.style = "display: none;";
|
||||||
}
|
}
|
||||||
|
|
||||||
// replace 'Enable WiFi' button with 'Deploy Access Point' button
|
// update ui for wifi client mode (status: online)
|
||||||
PEACH_NETWORK.clientMode = function() {
|
PEACH_NETWORK.clientOnline = function() {
|
||||||
// create Deploy Access Point button and add it to button div
|
console.log('Activating Client Mode');
|
||||||
var apButton = document.createElement("A");
|
|
||||||
apButton.className = "button center";
|
|
||||||
apButton.href = "/settings/network/ap/activate";
|
|
||||||
apButton.id = "deployAccessPoint";
|
|
||||||
var label = "Deploy Access Point";
|
|
||||||
var buttonText = document.createTextNode(label);
|
|
||||||
apButton.appendChild(buttonText);
|
|
||||||
|
|
||||||
// append the new button to the buttons div
|
// update network mode and status (icon & label)
|
||||||
let buttons = document.getElementById("buttons");
|
let i = document.getElementById("netModeIcon");
|
||||||
buttons.appendChild(apButton);
|
i.className = "center icon icon-active";
|
||||||
|
i.src = "icons/wifi.svg";
|
||||||
|
let l = document.getElementById("netModeLabel");
|
||||||
|
l.textContent = "ONLINE";
|
||||||
|
|
||||||
// remove the old 'Enable Wifi' button
|
// TODO: think about updates for buttons (transition from ap mode)
|
||||||
let wifiButton = document.getElementById("connectWifi");
|
}
|
||||||
wifiButton.remove();
|
|
||||||
|
// update ui for wifi client mode (status: offline)
|
||||||
|
PEACH_NETWORK.clientOffline = function() {
|
||||||
|
console.log('Activating Client Mode');
|
||||||
|
|
||||||
|
// update network mode and status (icon & label)
|
||||||
|
let i = document.getElementById("netModeIcon");
|
||||||
|
i.className = "center icon icon-inactive";
|
||||||
|
i.src = "icons/wifi.svg";
|
||||||
|
let l = document.getElementById("netModeLabel");
|
||||||
|
l.textContent = "OFFLINE";
|
||||||
|
|
||||||
|
// TODO: think about updates for buttons (transition from ap mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// display a message by appending a paragraph element
|
||||||
|
PEACH_NETWORK.flashMsg = function(status, msg) {
|
||||||
|
// set the class of the element according to status
|
||||||
|
var elementClass;
|
||||||
|
if (status === "success") {
|
||||||
|
elementClass = "capsule center-text flash-message font-success";
|
||||||
|
} else if (status === "info") {
|
||||||
|
elementClass = "capsule center-text flash-message font-info";
|
||||||
|
} else {
|
||||||
|
elementClass = "capsule center-text flash-message font-failure";
|
||||||
|
};
|
||||||
|
|
||||||
|
var flashElement = document.getElementById("flashMsg");
|
||||||
|
// if flashElement exists, update the class & text
|
||||||
|
if (flashElement) {
|
||||||
|
flashElement.className = elementClass;
|
||||||
|
flashElement.innerText = msg;
|
||||||
|
// if flashElement does not exist, create it, set id, class, text & append
|
||||||
|
} else {
|
||||||
|
// create new div for flash message
|
||||||
|
var flashDiv = document.createElement("DIV");
|
||||||
|
// set div attributes
|
||||||
|
flashDiv.id = "flashMsg";
|
||||||
|
flashDiv.className = elementClass;
|
||||||
|
// add json response message to flash message div
|
||||||
|
var flashMsg = document.createTextNode(msg);
|
||||||
|
flashDiv.appendChild(flashMsg);
|
||||||
|
// insert the flash message div above the three icon grid div
|
||||||
|
var gridDiv = document.getElementById("gridDiv");
|
||||||
|
gridDiv.parentNode.insertBefore(flashDiv, gridDiv);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var networkInstance = PEACH_NETWORK;
|
var networkInstance = PEACH_NETWORK;
|
||||||
networkInstance.activateAp();
|
networkInstance.activateAp();
|
||||||
networkInstance.activateClient();
|
networkInstance.activateClient();
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
async function exampleFetch() {
|
||||||
|
const response = await fetch('/api/v1/network/state');
|
||||||
|
const myJson = await response.json();
|
||||||
|
//const jsonData = JSON.parse(myJson);
|
||||||
|
console.log(myJson.data.wlan0);
|
||||||
|
//var state = document.createElement("P");
|
||||||
|
//state.innerText = ""jsonData.wlan0;
|
||||||
|
//document.body.appendChild(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
exampleFetch()
|
||||||
|
|
||||||
|
*/
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
|
|
||||||
behavioural layer for the `network_detail.html.tera` template,
|
behavioural layer for the `network_detail.html.tera` template,
|
||||||
corresponding to the web route `/settings/network/wifi?<ssid>`
|
corresponding to the web route `/network/wifi?<ssid>`
|
||||||
|
|
||||||
- intercept button clicks for connect, disconnect and forget
|
- intercept button clicks for connect, disconnect and forget
|
||||||
- perform json api call
|
- perform json api call
|
||||||
@ -12,6 +12,7 @@ methods:
|
|||||||
PEACH_NETWORK.connect();
|
PEACH_NETWORK.connect();
|
||||||
PEACH_NETWORK.disconnect();
|
PEACH_NETWORK.disconnect();
|
||||||
PEACH_NETWORK.forget();
|
PEACH_NETWORK.forget();
|
||||||
|
PEACH_NETWORK.flashMsg(status, msg);
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -32,7 +33,7 @@ PEACH_NETWORK.connect = function() {
|
|||||||
// perform json serialization
|
// perform json serialization
|
||||||
var jsonData = JSON.stringify(ssidData);
|
var jsonData = JSON.stringify(ssidData);
|
||||||
// write in-progress status message to ui
|
// write in-progress status message to ui
|
||||||
PEACH.flashMsg("info", "Connecting to access point...");
|
PEACH_NETWORK.flashMsg("info", "Connecting to access point...");
|
||||||
// send add_wifi POST request
|
// send add_wifi POST request
|
||||||
fetch("/api/v1/network/wifi/connect", {
|
fetch("/api/v1/network/wifi/connect", {
|
||||||
method: "post",
|
method: "post",
|
||||||
@ -46,7 +47,7 @@ PEACH_NETWORK.connect = function() {
|
|||||||
})
|
})
|
||||||
.then( (jsonData) => {
|
.then( (jsonData) => {
|
||||||
// write json response message to ui
|
// write json response message to ui
|
||||||
PEACH.flashMsg(jsonData.status, jsonData.msg);
|
PEACH_NETWORK.flashMsg(jsonData.status, jsonData.msg);
|
||||||
})
|
})
|
||||||
}, false);
|
}, false);
|
||||||
};
|
};
|
||||||
@ -68,7 +69,7 @@ PEACH_NETWORK.disconnect = function() {
|
|||||||
// perform json serialization
|
// perform json serialization
|
||||||
var jsonData = JSON.stringify(ssidData);
|
var jsonData = JSON.stringify(ssidData);
|
||||||
// write in-progress status message to ui
|
// write in-progress status message to ui
|
||||||
PEACH.flashMsg("info", "Disconnecting from access point...");
|
PEACH_NETWORK.flashMsg("info", "Disconnecting from access point...");
|
||||||
// send disconnect_wifi POST request
|
// send disconnect_wifi POST request
|
||||||
fetch("/api/v1/network/wifi/disconnect", {
|
fetch("/api/v1/network/wifi/disconnect", {
|
||||||
method: "post",
|
method: "post",
|
||||||
@ -82,7 +83,7 @@ PEACH_NETWORK.disconnect = function() {
|
|||||||
})
|
})
|
||||||
.then( (jsonData) => {
|
.then( (jsonData) => {
|
||||||
// write json response message to ui
|
// write json response message to ui
|
||||||
PEACH.flashMsg(jsonData.status, jsonData.msg);
|
PEACH_NETWORK.flashMsg(jsonData.status, jsonData.msg);
|
||||||
})
|
})
|
||||||
}, false);
|
}, false);
|
||||||
};
|
};
|
||||||
@ -104,7 +105,7 @@ PEACH_NETWORK.forget = function() {
|
|||||||
// perform json serialization
|
// perform json serialization
|
||||||
var jsonData = JSON.stringify(ssidData);
|
var jsonData = JSON.stringify(ssidData);
|
||||||
// write in-progress status message to ui
|
// write in-progress status message to ui
|
||||||
PEACH.flashMsg("info", "Removing credentials for access point...");
|
PEACH_NETWORK.flashMsg("info", "Removing credentials for access point...");
|
||||||
// send forget_ap POST request
|
// send forget_ap POST request
|
||||||
fetch("/api/v1/network/wifi/forget", {
|
fetch("/api/v1/network/wifi/forget", {
|
||||||
method: "post",
|
method: "post",
|
||||||
@ -118,13 +119,48 @@ PEACH_NETWORK.forget = function() {
|
|||||||
})
|
})
|
||||||
.then( (jsonData) => {
|
.then( (jsonData) => {
|
||||||
// write json response message to ui
|
// write json response message to ui
|
||||||
PEACH.flashMsg(jsonData.status, jsonData.msg);
|
PEACH_NETWORK.flashMsg(jsonData.status, jsonData.msg);
|
||||||
})
|
})
|
||||||
}, false);
|
}, false);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// display a message by appending a paragraph element
|
||||||
|
PEACH_NETWORK.flashMsg = function(status, msg) {
|
||||||
|
// set the class of the element according to status
|
||||||
|
var elementClass;
|
||||||
|
if (status === "success") {
|
||||||
|
elementClass = "capsule center-text flash-message font-success";
|
||||||
|
} else if (status === "info") {
|
||||||
|
elementClass = "capsule center-text flash-message font-info";
|
||||||
|
} else {
|
||||||
|
elementClass = "capsule center-text flash-message font-failure";
|
||||||
|
};
|
||||||
|
|
||||||
|
var flashElement = document.getElementById("flashMsg");
|
||||||
|
// if flashElement exists, update the class & text
|
||||||
|
if (flashElement) {
|
||||||
|
flashElement.className = elementClass;
|
||||||
|
flashElement.innerText = msg;
|
||||||
|
// if flashElement does not exist, create it, set id, class, text & append
|
||||||
|
} else {
|
||||||
|
// create new div for flash message
|
||||||
|
var flashDiv = document.createElement("DIV");
|
||||||
|
// set div attributes
|
||||||
|
flashDiv.id = "flashMsg";
|
||||||
|
flashDiv.className = elementClass;
|
||||||
|
// add json response message to flash message div
|
||||||
|
var flashMsg = document.createTextNode(msg);
|
||||||
|
flashDiv.appendChild(flashMsg);
|
||||||
|
// insert the flash message div below the button div
|
||||||
|
var buttonDiv = document.getElementById("buttonDiv");
|
||||||
|
// flashDiv will be added to the end since buttonDiv is the last
|
||||||
|
// child within the parent element (card-container div)
|
||||||
|
buttonDiv.parentNode.insertBefore(flashDiv, buttonDiv.nextSibling);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var detailInstance = PEACH_NETWORK;
|
var detailInstance = PEACH_NETWORK;
|
||||||
detailInstance.connect();
|
detailInstance.connect();
|
||||||
detailInstance.disconnect();
|
detailInstance.disconnect();
|
||||||
|
@ -9,6 +9,7 @@ behavioural layer for the `network_modify.html.tera` template
|
|||||||
methods:
|
methods:
|
||||||
|
|
||||||
PEACH_NETWORK.modify();
|
PEACH_NETWORK.modify();
|
||||||
|
PEACH_NETWORK.flashMsg(status, msg);
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -32,7 +33,7 @@ PEACH_NETWORK.modify = function() {
|
|||||||
// perform json serialization
|
// perform json serialization
|
||||||
var jsonData = JSON.stringify(object);
|
var jsonData = JSON.stringify(object);
|
||||||
// write in-progress status message to ui
|
// write in-progress status message to ui
|
||||||
PEACH.flashMsg("info", "Updating WiFi password...");
|
PEACH_NETWORK.flashMsg("info", "Updating WiFi password...");
|
||||||
// send new_password POST request
|
// send new_password POST request
|
||||||
fetch("/api/v1/network/wifi/modify", {
|
fetch("/api/v1/network/wifi/modify", {
|
||||||
method: "post",
|
method: "post",
|
||||||
@ -46,11 +47,46 @@ PEACH_NETWORK.modify = function() {
|
|||||||
})
|
})
|
||||||
.then( (jsonData) => {
|
.then( (jsonData) => {
|
||||||
// write json response message to ui
|
// write json response message to ui
|
||||||
PEACH.flashMsg(jsonData.status, jsonData.msg);
|
PEACH_NETWORK.flashMsg(jsonData.status, jsonData.msg);
|
||||||
})
|
})
|
||||||
}, false);
|
}, false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// display a message by appending a paragraph element
|
||||||
|
PEACH_NETWORK.flashMsg = function(status, msg) {
|
||||||
|
// set the class of the element according to status
|
||||||
|
var elementClass;
|
||||||
|
if (status === "success") {
|
||||||
|
elementClass = "capsule center-text flash-message font-success";
|
||||||
|
} else if (status === "info") {
|
||||||
|
elementClass = "capsule center-text flash-message font-info";
|
||||||
|
} else {
|
||||||
|
elementClass = "capsule center-text flash-message font-failure";
|
||||||
|
};
|
||||||
|
|
||||||
|
var flashElement = document.getElementById("flashMsg");
|
||||||
|
// if flashElement exists, update the class & text
|
||||||
|
if (flashElement) {
|
||||||
|
flashElement.className = elementClass;
|
||||||
|
flashElement.innerText = msg;
|
||||||
|
// if flashElement does not exist, create it, set id, class, text & append
|
||||||
|
} else {
|
||||||
|
// create new div for flash message
|
||||||
|
var flashDiv = document.createElement("DIV");
|
||||||
|
// set div attributes
|
||||||
|
flashDiv.id = "flashMsg";
|
||||||
|
flashDiv.className = elementClass;
|
||||||
|
// add json response message to flash message div
|
||||||
|
var flashMsg = document.createTextNode(msg);
|
||||||
|
flashDiv.appendChild(flashMsg);
|
||||||
|
// insert the flash message div below the button div
|
||||||
|
var buttonDiv = document.getElementById("buttonDiv");
|
||||||
|
// flashDiv will be added to the end since buttonDiv is the last
|
||||||
|
// child within the parent element (card-container div)
|
||||||
|
buttonDiv.parentNode.insertBefore(flashDiv, buttonDiv.nextSibling);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var modifyInstance = PEACH_NETWORK;
|
var modifyInstance = PEACH_NETWORK;
|
||||||
modifyInstance.modify();
|
modifyInstance.modify();
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
|
|
||||||
behavioural layer for the `network_usage.html.tera` template,
|
behavioural layer for the `network_usage.html.tera` template,
|
||||||
corresponding to the web route `/settings/network/wifi/usage`
|
corresponding to the web route `/network/wifi/usage`
|
||||||
|
|
||||||
- intercept form submissions
|
- intercept form submissions
|
||||||
- perform json api calls
|
- perform json api calls
|
||||||
@ -13,6 +13,7 @@ methods:
|
|||||||
PEACH_NETWORK.resetUsage();
|
PEACH_NETWORK.resetUsage();
|
||||||
PEACH_NETWORK.toggleWarning();
|
PEACH_NETWORK.toggleWarning();
|
||||||
PEACH_NETWORK.toggleCutoff();
|
PEACH_NETWORK.toggleCutoff();
|
||||||
|
PEACH_NETWORK.flashMsg(status, msg);
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -50,7 +51,7 @@ PEACH_NETWORK.updateAlerts = function() {
|
|||||||
})
|
})
|
||||||
.then( (jsonData) => {
|
.then( (jsonData) => {
|
||||||
// write json response message to ui
|
// write json response message to ui
|
||||||
PEACH.flashMsg(jsonData.status, jsonData.msg);
|
PEACH_NETWORK.flashMsg(jsonData.status, jsonData.msg);
|
||||||
})
|
})
|
||||||
}, false);
|
}, false);
|
||||||
});
|
});
|
||||||
@ -78,7 +79,7 @@ PEACH_NETWORK.resetUsage = function() {
|
|||||||
.then( (jsonData) => {
|
.then( (jsonData) => {
|
||||||
console.log(jsonData.msg);
|
console.log(jsonData.msg);
|
||||||
// write json response message to ui
|
// write json response message to ui
|
||||||
PEACH.flashMsg(jsonData.status, jsonData.msg);
|
PEACH_NETWORK.flashMsg(jsonData.status, jsonData.msg);
|
||||||
// if reset is successful, update the ui
|
// if reset is successful, update the ui
|
||||||
if (jsonData.status === "success") {
|
if (jsonData.status === "success") {
|
||||||
console.log(jsonData.data);
|
console.log(jsonData.data);
|
||||||
@ -132,6 +133,39 @@ PEACH_NETWORK.toggleCutoff = function() {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// display a message by appending a paragraph element
|
||||||
|
PEACH_NETWORK.flashMsg = function(status, msg) {
|
||||||
|
// set the class of the element according to status
|
||||||
|
var elementClass;
|
||||||
|
if (status === "success") {
|
||||||
|
elementClass = "capsule center-text flash-message font-success";
|
||||||
|
} else if (status === "info") {
|
||||||
|
elementClass = "capsule center-text flash-message font-info";
|
||||||
|
} else {
|
||||||
|
elementClass = "capsule center-text flash-message font-failure";
|
||||||
|
};
|
||||||
|
|
||||||
|
var flashElement = document.getElementById("flashMsg");
|
||||||
|
// if flashElement exists, update the class & text
|
||||||
|
if (flashElement) {
|
||||||
|
flashElement.className = elementClass;
|
||||||
|
flashElement.innerText = msg;
|
||||||
|
// if flashElement does not exist, create it, set id, class, text & append
|
||||||
|
} else {
|
||||||
|
// create new div for flash message
|
||||||
|
var flashDiv = document.createElement("DIV");
|
||||||
|
// set div attributes
|
||||||
|
flashDiv.id = "flashMsg";
|
||||||
|
flashDiv.className = elementClass;
|
||||||
|
// add json response message to flash message div
|
||||||
|
var flashMsg = document.createTextNode(msg);
|
||||||
|
flashDiv.appendChild(flashMsg);
|
||||||
|
// insert the flash message div below the button div
|
||||||
|
var buttonDiv = document.getElementById("buttonDiv");
|
||||||
|
buttonDiv.parentNode.insertBefore(flashDiv, buttonDiv.nextSibling);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var usageInstance = PEACH_NETWORK;
|
var usageInstance = PEACH_NETWORK;
|
||||||
usageInstance.resetUsage();
|
usageInstance.resetUsage();
|
||||||
usageInstance.toggleWarning();
|
usageInstance.toggleWarning();
|
||||||
|
@ -11,6 +11,7 @@ methods:
|
|||||||
|
|
||||||
PEACH_DEVICE.reboot();
|
PEACH_DEVICE.reboot();
|
||||||
PEACH_DEVICE.shutdown();
|
PEACH_DEVICE.shutdown();
|
||||||
|
PEACH_DEVICE.flashMsg(status, msg);
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -25,7 +26,7 @@ PEACH_DEVICE.reboot = function() {
|
|||||||
// prevent redirect on button press (default behavior)
|
// prevent redirect on button press (default behavior)
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
// write reboot flash message
|
// write reboot flash message
|
||||||
PEACH.flashMsg("success", "Rebooting the device...");
|
PEACH_DEVICE.flashMsg("success", "Rebooting the device...");
|
||||||
// send reboot_device POST request
|
// send reboot_device POST request
|
||||||
fetch("/api/v1/admin/reboot", {
|
fetch("/api/v1/admin/reboot", {
|
||||||
method: "post",
|
method: "post",
|
||||||
@ -40,7 +41,7 @@ PEACH_DEVICE.reboot = function() {
|
|||||||
.then( (jsonData) => {
|
.then( (jsonData) => {
|
||||||
console.log(jsonData.msg);
|
console.log(jsonData.msg);
|
||||||
// write json response message to ui
|
// write json response message to ui
|
||||||
PEACH.flashMsg(jsonData.status, jsonData.msg);
|
PEACH_DEVICE.flashMsg(jsonData.status, jsonData.msg);
|
||||||
})
|
})
|
||||||
}, false);
|
}, false);
|
||||||
}
|
}
|
||||||
@ -56,7 +57,7 @@ PEACH_DEVICE.shutdown = function() {
|
|||||||
// prevent form submission (default behavior)
|
// prevent form submission (default behavior)
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
// write shutdown flash message
|
// write shutdown flash message
|
||||||
PEACH.flashMsg("success", "Shutting down the device...");
|
PEACH_DEVICE.flashMsg("success", "Shutting down the device...");
|
||||||
// send shutdown_device POST request
|
// send shutdown_device POST request
|
||||||
fetch("/api/v1/shutdown", {
|
fetch("/api/v1/shutdown", {
|
||||||
method: "post",
|
method: "post",
|
||||||
@ -71,13 +72,48 @@ PEACH_DEVICE.shutdown = function() {
|
|||||||
.then( (jsonData) => {
|
.then( (jsonData) => {
|
||||||
console.log(jsonData.msg);
|
console.log(jsonData.msg);
|
||||||
// write json response message to ui
|
// write json response message to ui
|
||||||
PEACH.flashMsg(jsonData.status, jsonData.msg);
|
PEACH_DEVICE.flashMsg(jsonData.status, jsonData.msg);
|
||||||
})
|
})
|
||||||
}, false);
|
}, false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// display a message by appending a paragraph element
|
||||||
|
PEACH_DEVICE.flashMsg = function(status, msg) {
|
||||||
|
// set the class of the element according to status
|
||||||
|
var elementClass;
|
||||||
|
if (status === "success") {
|
||||||
|
elementClass = "capsule center-text flash-message font-success";
|
||||||
|
} else if (status === "info") {
|
||||||
|
elementClass = "capsule center-text flash-message font-info";
|
||||||
|
} else {
|
||||||
|
elementClass = "capsule center-text flash-message font-failure";
|
||||||
|
};
|
||||||
|
|
||||||
|
var flashElement = document.getElementById("flashMsg");
|
||||||
|
// if flashElement exists, update the class & text
|
||||||
|
if (flashElement) {
|
||||||
|
flashElement.className = elementClass;
|
||||||
|
flashElement.innerText = msg;
|
||||||
|
// if flashElement does not exist, create it, set id, class, text & append
|
||||||
|
} else {
|
||||||
|
// create new div for flash message
|
||||||
|
var flashDiv = document.createElement("DIV");
|
||||||
|
// set div attributes
|
||||||
|
flashDiv.id = "flashMsg";
|
||||||
|
flashDiv.className = elementClass;
|
||||||
|
// add json response message to flash message div
|
||||||
|
var flashMsg = document.createTextNode(msg);
|
||||||
|
flashDiv.appendChild(flashMsg);
|
||||||
|
// insert the flash message div below the button div
|
||||||
|
var buttonDiv = document.getElementById("buttonDiv");
|
||||||
|
// flashDiv will be added to the end since buttonDiv is the last
|
||||||
|
// child within the parent element (card-container div)
|
||||||
|
buttonDiv.parentNode.insertBefore(flashDiv, buttonDiv.nextSibling);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var deviceInstance = PEACH_DEVICE;
|
var deviceInstance = PEACH_DEVICE;
|
||||||
deviceInstance.reboot();
|
deviceInstance.reboot();
|
||||||
deviceInstance.shutdown();
|
deviceInstance.shutdown();
|
||||||
|
@ -2,17 +2,15 @@
|
|||||||
* behavioural layer for the `reset_password.html.tera` template,
|
* behavioural layer for the `reset_password.html.tera` template,
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var PEACH_AUTH = {};
|
|
||||||
|
|
||||||
// catch click of 'Save' button and make POST request
|
// catch click of 'Save' button and make POST request
|
||||||
PEACH_AUTH.resetPassword = function() {
|
PEACH.add = function() {
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
document.body.addEventListener('submit', function(e) {
|
document.body.addEventListener('submit', function(e) {
|
||||||
// prevent redirect on button press (default behavior)
|
// prevent redirect on button press (default behavior)
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
// capture form data
|
// capture form data
|
||||||
var formElement = document.querySelector("form");
|
var formElement = document.querySelector("form");
|
||||||
// create form data object from the changePassword form element
|
// create form data object from the wifiCreds form element
|
||||||
var formData = new FormData(formElement);
|
var formData = new FormData(formElement);
|
||||||
var object = {};
|
var object = {};
|
||||||
// assign values from form
|
// assign values from form
|
||||||
@ -43,5 +41,5 @@ PEACH_AUTH.resetPassword = function() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var resetPassInstance = PEACH_AUTH;
|
var addInstance = PEACH;
|
||||||
resetPassInstance.resetPassword();
|
addInstance.add();
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
{%- extends "nav" -%}
|
{%- extends "nav" -%}
|
||||||
{%- block card %}
|
{%- block card %}
|
||||||
<div class="card center">
|
<div class="card center">
|
||||||
<div class="capsule-container">
|
<div class="card-container capsule info-border">
|
||||||
<div class="capsule info-border">
|
<p>No PeachCloud resource exists for this URL. Please ensure that the URL in the address bar is correct.</p>
|
||||||
<p>No PeachCloud resource exists for this URL. Please ensure that the URL in the address bar is correct.</p>
|
<p>Click the back arrow in the top-left or the PeachCloud logo at the bottom of your screen to return Home.</p>
|
||||||
<p>Click the back arrow in the top-left or the PeachCloud logo at the bottom of your screen to return Home.</p>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
{%- endblock card -%}
|
{%- endblock card -%}
|
||||||
|
@ -4,22 +4,47 @@
|
|||||||
<div class="card center">
|
<div class="card center">
|
||||||
<div class="form-container">
|
<div class="form-container">
|
||||||
<form id="changePassword" action="/settings/change_password" method="post">
|
<form id="changePassword" action="/settings/change_password" method="post">
|
||||||
<!-- input for current password -->
|
<div class="input-wrapper">
|
||||||
<input id="currentPassword" class="center input" name="current_password" type="password" placeholder="Current password" title="Current password" autofocus>
|
<!-- input for old password -->
|
||||||
<!-- input for new password -->
|
<label id="old_password" class="label-small input-label font-near-black">
|
||||||
<input id="newPassword" class="center input" name="new_password1" type="password" placeholder="New password" title="New password">
|
<label class="label-small input-label font-gray" for="old_password" style="padding-top: 0.25rem;">Old Password</label>
|
||||||
<!-- input for duplicate new password -->
|
<input id="old_password" class="form-input" style="margin-bottom: 0;"
|
||||||
<input id="newPasswordDuplicate" class="center input" name="new_password2" type="password" placeholder="Re-enter new password" title="New password duplicate">
|
name="old_password" type="password" title="old password" value="">
|
||||||
<div id="buttonDiv">
|
</label>
|
||||||
<input id="savePassword" class="button button-primary center" title="Add" type="submit" value="Save">
|
|
||||||
<a class="button button-secondary center" href="/settings/admin" title="Cancel">Cancel</a>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="input-wrapper">
|
||||||
|
<!-- input for new password1 -->
|
||||||
|
<label id="new_password1" class="label-small input-label font-near-black">
|
||||||
|
<label class="label-small input-label font-gray" for="new_password1" style="padding-top: 0.25rem;">Enter New Password</label>
|
||||||
|
<input id="new_password1" class="form-input" style="margin-bottom: 0;"
|
||||||
|
name="new_password1" title="new_password1" type="password" value="">
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="input-wrapper">
|
||||||
|
<!-- input for new password2 -->
|
||||||
|
<label id="new_password2" class="label-small input-label font-near-black">
|
||||||
|
<label class="label-small input-label font-gray" for="new_password2" style="padding-top: 0.25rem;">Re-Enter New Password</label>
|
||||||
|
<input id="new_password2" class="form-input" style="margin-bottom: 0;"
|
||||||
|
name="new_password2" title="new_password2" type="password" value="">
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="buttonDiv">
|
||||||
|
<input id="changePasswordButton" class="button button-primary center" title="Add" type="submit" value="Save">
|
||||||
|
</div>
|
||||||
|
<a class="button button-secondary center" href="/settings/admin" title="Cancel">Cancel</a>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<!-- FLASH MESSAGE -->
|
<!-- FLASH MESSAGE -->
|
||||||
{% include "snippets/flash_message" %}
|
{% include "snippets/flash_message" %}
|
||||||
|
|
||||||
<!-- NO SCRIPT FOR WHEN JS IS DISABLED -->
|
<!-- NO SCRIPT FOR WHEN JS IS DISABLED -->
|
||||||
{% include "snippets/noscript" %}
|
{% include "snippets/noscript" %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript" src="/js/change_password.js"></script>
|
<script type="text/javascript" src="/js/change_password.js"></script>
|
||||||
{%- endblock card -%}
|
{%- endblock card -%}
|
||||||
|
@ -1,20 +1,25 @@
|
|||||||
{%- extends "nav" -%}
|
{%- extends "nav" -%}
|
||||||
{%- block card %}
|
{%- block card %}
|
||||||
<!-- PASSWORD RESET REQUEST CARD -->
|
<!--PUBLIC PAGE FOR SENDING A NEW TEMPORARY PASSWORD TO BE USED TO RESET YOUR PASSWORD -->
|
||||||
<div class="card center">
|
<div class="card center">
|
||||||
<div class="capsule capsule-container info-border">
|
<p class="text-notice" style="width: 80%; margin:auto; margin-bottom: 35px; margin-top: 20px;">
|
||||||
<p class="card-text">Click the button below to send a new temporary password which can be used to change your device password.
|
Click the button below to send a new temporary password which can be used to change your device password.
|
||||||
</br></br>
|
<br/><br/>
|
||||||
The temporary password will be sent in an SSB private message to the admin of this device.</p>
|
The temporary password will be sent in an SSB private message to the admin of this device.
|
||||||
</div>
|
</p>
|
||||||
|
|
||||||
<form id="sendPasswordReset" action="/send_password_reset" method="post">
|
<form id="sendPasswordReset" action="/send_password_reset" method="post">
|
||||||
<div id="buttonDiv">
|
<div id="buttonDiv">
|
||||||
<input class="button button-primary center" style="margin-top: 1rem;" type="submit" value="Send Password Reset" title="Send Password Reset Link"/>
|
<input type="submit" class="button center button-secondary" value="Send Password Reset" title="Send Password Reset Link"/>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<!-- FLASH MESSAGE -->
|
<!-- FLASH MESSAGE -->
|
||||||
{% include "snippets/flash_message" %}
|
{% include "snippets/flash_message" %}
|
||||||
|
|
||||||
<!-- NO SCRIPT FOR WHEN JS IS DISABLED -->
|
<!-- NO SCRIPT FOR WHEN JS IS DISABLED -->
|
||||||
{% include "snippets/noscript" %}
|
{% include "snippets/noscript" %}
|
||||||
|
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{%- endblock card -%}
|
{%- endblock card -%}
|
||||||
|
@ -2,10 +2,12 @@
|
|||||||
{%- block card %}
|
{%- block card %}
|
||||||
<!-- ADMIN SETTINGS MENU -->
|
<!-- ADMIN SETTINGS MENU -->
|
||||||
<div class="card center">
|
<div class="card center">
|
||||||
<!-- BUTTONS -->
|
<div class="card-container">
|
||||||
<div id="settingsButtons">
|
<!-- BUTTONS -->
|
||||||
<a id="change" class="button button-primary center" href="/settings/admin/change_password" title="Change Password">Change Password</a>
|
<div id="settingsButtons">
|
||||||
<a id="configure" class="button button-primary center" href="/settings/admin/configure" title="Configure Admin">Configure Admin</a>
|
<a id="change" class="button button-primary center" href="/settings/admin/change_password" title="Change Password">Change Password</a>
|
||||||
|
<a id="configure" class="button button-primary center" href="/settings/admin/configure" title="Configure Admin">Configure Admin</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{%- endblock card -%}
|
{%- endblock card -%}
|
||||||
|
@ -2,11 +2,13 @@
|
|||||||
{%- block card %}
|
{%- block card %}
|
||||||
<!-- SETTINGS MENU -->
|
<!-- SETTINGS MENU -->
|
||||||
<div class="card center">
|
<div class="card center">
|
||||||
<!-- BUTTONS -->
|
<div class="card-container">
|
||||||
<div id="settingsButtons">
|
<!-- BUTTONS -->
|
||||||
<a id="network" class="button button-primary center" href="/settings/network" title="Network Settings">Network</a>
|
<div id="settingsButtons">
|
||||||
<a id="scuttlebutt" class="button button-primary center" href="/settings/scuttlebutt" title="Scuttlebutt Settings">Scuttlebutt</a>
|
<a id="network" class="button button-primary center" href="/settings/network" title="Network Settings">Network</a>
|
||||||
<a id="admin" class="button button-primary center" href="/settings/admin" title="Administrator Settings">Administration</a>
|
<a id="scuttlebutt" class="button button-primary center" href="/settings/scuttlebutt" title="Scuttlebutt Settings">Scuttlebutt</a>
|
||||||
|
<a id="admin" class="button button-primary center" href="/settings/admin" title="Administrator Settings">Administration</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{%- endblock card -%}
|
{%- endblock card -%}
|
||||||
|
@ -2,17 +2,19 @@
|
|||||||
{%- block card %}
|
{%- block card %}
|
||||||
<!-- SCUTTLEBUTT SETTINGS MENU -->
|
<!-- SCUTTLEBUTT SETTINGS MENU -->
|
||||||
<div class="card center">
|
<div class="card center">
|
||||||
<!-- BUTTONS -->
|
<div class="card-container">
|
||||||
<div id="settingsButtons">
|
<!-- BUTTONS -->
|
||||||
<a id="networkKey" class="button button-primary center" href="/settings/scuttlebutt/network_key" title="Set Network Key">Set Network Key</a>
|
<div id="settingsButtons">
|
||||||
<a id="replicationHops" class="button button-primary center" href="/settings/scuttlebutt/hops" title="Set Replication Hops">Set Replication Hops</a>
|
<a id="networkKey" class="button button-primary center" href="/settings/scuttlebutt/network_key" title="Set Network Key">Set Network Key</a>
|
||||||
<a id="removeFeeds" class="button button-primary center" href="/settings/scuttlebutt/remove_feeds" title="Remove Blocked Feeds">Remove Blocked Feeds</a>
|
<a id="replicationHops" class="button button-primary center" href="/settings/scuttlebutt/hops" title="Set Replication Hops">Set Replication Hops</a>
|
||||||
<a id="setDirectory" class="button button-primary center" href="/settings/scuttlebutt/set_directory" title="Set Database Directory">Set Database Directory</a>
|
<a id="removeFeeds" class="button button-primary center" href="/settings/scuttlebutt/remove_feeds" title="Remove Blocked Feeds">Remove Blocked Feeds</a>
|
||||||
<a id="checkFilesystem" class="button button-primary center" href="/settings/scuttlebutt/check_fs" title="Check Filesystem">Check Filesystem</a>
|
<a id="setDirectory" class="button button-primary center" href="/settings/scuttlebutt/set_directory" title="Set Database Directory">Set Database Directory</a>
|
||||||
<a id="repairFilesystem" class="button button-primary center" href="/settings/scuttlebutt/repair" title="Repair Filesystem">Repair Filesystem</a>
|
<a id="checkFilesystem" class="button button-primary center" href="/settings/scuttlebutt/check_fs" title="Check Filesystem">Check Filesystem</a>
|
||||||
<a id="disable" class="button button-primary center" href="/settings/scuttlebutt/disable" title="Disable Sbot">Disable Sbot</a>
|
<a id="repairFilesystem" class="button button-primary center" href="/settings/scuttlebutt/repair" title="Repair Filesystem">Repair Filesystem</a>
|
||||||
<a id="enable" class="button button-primary center" href="/settings/scuttlebutt/enable" title="Enable Sbot">Enable Sbot</a>
|
<a id="disable" class="button button-primary center" href="/settings/scuttlebutt/disable" title="Disable Sbot">Disable Sbot</a>
|
||||||
<a id="restart" class="button button-primary center" href="/settings/scuttlebutt/restart" title="Restart Sbot">Restart Sbot</a>
|
<a id="enable" class="button button-primary center" href="/settings/scuttlebutt/enable" title="Enable Sbot">Enable Sbot</a>
|
||||||
|
<a id="restart" class="button button-primary center" href="/settings/scuttlebutt/restart" title="Restart Sbot">Restart Sbot</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{%- endblock card -%}
|
{%- endblock card -%}
|
||||||
|
Reference in New Issue
Block a user