diff --git a/Cargo.lock b/Cargo.lock index 1e295c3..35610f6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -80,15 +80,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "ansi_term" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" -dependencies = [ - "winapi 0.3.9", -] - [[package]] name = "ansi_term" version = "0.12.1" @@ -114,9 +105,9 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "648ed8c8d2ce5409ccd57453d9d1b214b342a0d69376a6feda1fd6cae3299308" dependencies = [ - "proc-macro2 1.0.32", + "proc-macro2 1.0.33", "quote 1.0.10", - "syn 1.0.81", + "syn 1.0.82", ] [[package]] @@ -125,16 +116,16 @@ version = "0.1.51" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44318e776df68115a881de9a8fd1b9e53368d7a4a5ce4cc48517da3393233a5e" dependencies = [ - "proc-macro2 1.0.32", + "proc-macro2 1.0.33", "quote 1.0.10", - "syn 1.0.81", + "syn 1.0.82", ] [[package]] name = "atomic" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3410529e8288c463bedb5930f82833bc0c90e5d2fe639a56582a4d09220b281" +checksum = "b88d82667eca772c4aa12f0f1348b3ae643424c8876448f3f7bd5787032e234c" dependencies = [ "autocfg 1.0.1", ] @@ -204,15 +195,6 @@ dependencies = [ "safemem", ] -[[package]] -name = "base64" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" -dependencies = [ - "byteorder", -] - [[package]] name = "base64" version = "0.13.0" @@ -340,9 +322,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.71" +version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd" +checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" [[package]] name = "cfg-if" @@ -402,11 +384,11 @@ dependencies = [ [[package]] name = "clap" -version = "2.33.3" +version = "2.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" dependencies = [ - "ansi_term 0.11.0", + "ansi_term", "atty", "bitflags 1.3.2", "strsim", @@ -445,7 +427,7 @@ version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef196d5d972878a48da7decb7686eded338b4858fbabeed513d63a7c98b2b82d" dependencies = [ - "proc-macro2 1.0.32", + "proc-macro2 1.0.33", "quote 1.0.10", "unicode-xid 0.2.2", ] @@ -473,22 +455,6 @@ dependencies = [ "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]] name = "cpufeatures" version = "0.2.1" @@ -612,15 +578,15 @@ dependencies = [ [[package]] name = "derive_more" -version = "0.99.16" +version = "0.99.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40eebddd2156ce1bb37b20bbe5151340a31828b1f2d22ba4141f3531710e38df" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ "convert_case", - "proc-macro2 1.0.32", + "proc-macro2 1.0.33", "quote 1.0.10", - "rustc_version 0.3.3", - "syn 1.0.81", + "rustc_version 0.4.0", + "syn 1.0.82", ] [[package]] @@ -656,10 +622,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841ef46f4787d9097405cac4e70fb8644fc037b526e8c14054247c0263c400d0" dependencies = [ "bitflags 1.3.2", - "proc-macro2 1.0.32", + "proc-macro2 1.0.33", "proc-macro2-diagnostics", "quote 1.0.10", - "syn 1.0.81", + "syn 1.0.82", ] [[package]] @@ -818,9 +784,9 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" dependencies = [ - "proc-macro2 1.0.32", + "proc-macro2 1.0.33", "quote 1.0.10", - "syn 1.0.81", + "syn 1.0.82", "synstructure", ] @@ -936,9 +902,9 @@ checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" [[package]] name = "futures" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a12aa0eb539080d55c3f2d45a67c3b58b6b0773c1a3ca2dfec66d58c97fd66ca" +checksum = "8cd0210d8c325c245ff06fd95a3b13689a1a276ac8cfa8e8720cb840bfb84b9e" dependencies = [ "futures-channel", "futures-core", @@ -951,9 +917,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5da6ba8c3bb3c165d3c7319fc1cc8304facf1fb8db99c5de877183c08a273888" +checksum = "7fc8cd39e3dbf865f7340dce6a2d401d24fd37c6fe6c4f0ee0de8bfca2252d27" dependencies = [ "futures-core", "futures-sink", @@ -961,9 +927,9 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d1c26957f23603395cd326b0ffe64124b818f4449552f960d815cfba83a53d" +checksum = "629316e42fe7c2a0b9a65b47d159ceaa5453ab14e8f0a3c5eedbb8cd55b4a445" [[package]] name = "futures-cpupool" @@ -977,9 +943,9 @@ dependencies = [ [[package]] name = "futures-executor" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45025be030969d763025784f7f355043dc6bc74093e4ecc5000ca4dc50d8745c" +checksum = "7b808bf53348a36cab739d7e04755909b9fcaaa69b7d7e588b37b6ec62704c97" dependencies = [ "futures-core", "futures-task", @@ -989,42 +955,39 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "522de2a0fe3e380f1bc577ba0474108faf3f6b18321dbf60b3b9c39a75073377" +checksum = "e481354db6b5c353246ccf6a728b0c5511d752c08da7260546fc0933869daa11" [[package]] name = "futures-macro" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18e4a4b95cea4b4ccbcf1c5675ca7c4ee4e9e75eb79944d07defde18068f79bb" +checksum = "a89f17b21645bc4ed773c69af9c9a0effd4a3f1a3876eadd453469f8854e7fdd" dependencies = [ - "autocfg 1.0.1", - "proc-macro-hack", - "proc-macro2 1.0.32", + "proc-macro2 1.0.33", "quote 1.0.10", - "syn 1.0.81", + "syn 1.0.82", ] [[package]] name = "futures-sink" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36ea153c13024fe480590b3e3d4cad89a0cfacecc24577b68f86c6ced9c2bc11" +checksum = "996c6442437b62d21a32cd9906f9c41e7dc1e19a9579843fad948696769305af" [[package]] name = "futures-task" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d3d00f4eddb73e498a54394f228cd55853bdf059259e8e7bc6e69d408892e99" +checksum = "dabf1872aaab32c886832f2276d2f5399887e2bd613698a02359e4ea83f8de12" [[package]] name = "futures-util" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36568465210a3a6ee45e1f165136d68671471a501e632e9a98d96872222b5481" +checksum = "41d22213122356472061ac0f1ab2cee28d2bac8491410fd68c2af53d1cedb83e" dependencies = [ - "autocfg 1.0.1", "futures 0.1.31", "futures-channel", "futures-core", @@ -1035,8 +998,6 @@ dependencies = [ "memchr", "pin-project-lite", "pin-utils", - "proc-macro-hack", - "proc-macro-nested", "slab 0.4.5", ] @@ -1212,7 +1173,7 @@ dependencies = [ "http 0.2.5", "indexmap", "slab 0.4.5", - "tokio 1.13.0", + "tokio 1.14.0", "tokio-util", "tracing", ] @@ -1314,9 +1275,9 @@ checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503" [[package]] name = "httpdate" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6456b8a6c8f33fee7d958fcd1b60d55b11940a79e63ae87013e6d22e26034440" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "humansize" @@ -1339,25 +1300,6 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "hyper" version = "0.11.27" @@ -1372,7 +1314,7 @@ dependencies = [ "iovec", "language-tags", "log 0.4.14", - "mime 0.3.16", + "mime", "net2", "percent-encoding 1.0.1", "relay", @@ -1381,7 +1323,7 @@ dependencies = [ "tokio-io", "tokio-proto", "tokio-service", - "unicase 2.6.0", + "unicase", "want 0.0.4", ] @@ -1417,9 +1359,9 @@ dependencies = [ [[package]] name = "hyper" -version = "0.14.14" +version = "0.14.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b91bb1f221b6ea1f1e4371216b70f40748774c2fb5971b450c07773fb92d26b" +checksum = "436ec0091e4f20e655156a30a0df3770fe2900aa301e548e08446ec794b6953c" dependencies = [ "bytes 1.1.0", "futures-channel", @@ -1433,7 +1375,7 @@ dependencies = [ "itoa", "pin-project-lite", "socket2", - "tokio 1.13.0", + "tokio 1.14.0", "tower-service", "tracing", "want 0.3.0", @@ -1576,7 +1518,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2b99d4207e2a04fb4581746903c2bb7eb376f88de9c699d0f3e10feeac0cd3a" dependencies = [ "derive_more", - "futures 0.3.17", + "futures 0.3.18", "jsonrpc-core 18.0.0", "jsonrpc-pubsub 18.0.0", "log 0.4.14", @@ -1630,7 +1572,7 @@ version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14f7f76aef2d054868398427f6c54943cf3d1caa9a7ec7d0c38d69df97a965eb" dependencies = [ - "futures 0.3.17", + "futures 0.3.18", "futures-executor", "futures-util", "log 0.4.14", @@ -1659,7 +1601,7 @@ version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b51da17abecbdab3e3d4f26b01c5ec075e88d3abe3ab3b05dc9aa69392764ec0" dependencies = [ - "futures 0.3.17", + "futures 0.3.18", "jsonrpc-client-transports", ] @@ -1674,7 +1616,7 @@ dependencies = [ "jsonrpc-server-utils 11.0.0", "log 0.4.14", "net2", - "unicase 2.6.0", + "unicase", ] [[package]] @@ -1683,14 +1625,14 @@ version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1dea6e07251d9ce6a552abfb5d7ad6bc290a4596c8dcc3d795fae2bbdc1f3ff" dependencies = [ - "futures 0.3.17", - "hyper 0.14.14", + "futures 0.3.18", + "hyper 0.14.15", "jsonrpc-core 18.0.0", "jsonrpc-server-utils 18.0.0", "log 0.4.14", "net2", "parking_lot 0.11.2", - "unicase 2.6.0", + "unicase", ] [[package]] @@ -1711,7 +1653,7 @@ version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240f87695e6c6f62fb37f05c02c04953cf68d6408b8c1c89de85c7a0125b1011" dependencies = [ - "futures 0.3.17", + "futures 0.3.18", "jsonrpc-core 18.0.0", "lazy_static", "log 0.4.14", @@ -1734,7 +1676,7 @@ dependencies = [ "num_cpus", "tokio 0.1.22", "tokio-codec", - "unicase 2.6.0", + "unicase", ] [[package]] @@ -1744,15 +1686,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa4fdea130485b572c39a460d50888beb00afb3e35de23ccd7fad8ff19f0e0d4" dependencies = [ "bytes 1.1.0", - "futures 0.3.17", + "futures 0.3.18", "globset", "jsonrpc-core 18.0.0", "lazy_static", "log 0.4.14", - "tokio 1.13.0", + "tokio 1.14.0", "tokio-stream", "tokio-util", - "unicase 2.6.0", + "unicase", ] [[package]] @@ -1827,9 +1769,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.106" +version = "0.2.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a60553f9a9e039a333b4e9b20573b9e9b9c0bb3a11e201ccc48ef4283456d673" +checksum = "f98a04dce437184842841303488f70d0188c5f51437d2a834dc097eafa909a01" [[package]] name = "linked-hash-map" @@ -1908,9 +1850,9 @@ dependencies = [ [[package]] name = "loom" -version = "0.5.2" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2b9df80a3804094bf49bb29881d18f6f05048db72127e84e09c26fc7c2324f5" +checksum = "edc5c7d328e32cc4954e8e01193d7f0ef5ab257b5090b70a964e099a36034309" dependencies = [ "cfg-if 1.0.0", "generator", @@ -1929,9 +1871,9 @@ checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" [[package]] name = "matchers" -version = "0.0.1" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" dependencies = [ "regex-automata", ] @@ -1965,22 +1907,13 @@ dependencies = [ [[package]] name = "memoffset" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" dependencies = [ "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]] name = "mime" version = "0.3.16" @@ -1989,26 +1922,26 @@ checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" [[package]] name = "mini-internal" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac2f0e3cdd28b81bf077f464771717698e7ae3f0321c0086dd7ad241d6085ce7" +checksum = "926fbf1cd8695183e4712d1c77cdf2c5f7916f9f73ba103dca78ff2e6755ab47" dependencies = [ - "proc-macro2 1.0.32", + "proc-macro2 1.0.33", "quote 1.0.10", - "syn 1.0.81", + "syn 1.0.82", ] [[package]] name = "minimal-lexical" -version = "0.1.4" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c64630dcdd71f1a64c435f54885086a0de5d6a12d104d69b165fb7d5286d677" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniserde" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a116a35523bbd5b1712cefda270a03d1481263a114b6705cb1f02692651f76f" +checksum = "0767e42acf28c5e08fee73cb657bcb23432c3769cbdf3881a8cb69d8df5020df" dependencies = [ "itoa", "mini-internal", @@ -2119,9 +2052,9 @@ dependencies = [ "http 0.2.5", "httparse", "log 0.4.14", - "mime 0.3.16", + "mime", "spin", - "tokio 1.13.0", + "tokio 1.14.0", "tokio-util", "twoway", "version_check 0.9.3", @@ -2133,24 +2066,6 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "729eb334247daa1803e0a094d0a5c55711b85571179f5ec6e53eccfdf7008958" -[[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]] name = "nb" version = "0.1.3" @@ -2246,7 +2161,7 @@ dependencies = [ "cc", "cfg-if 1.0.0", "libc", - "memoffset 0.6.4", + "memoffset 0.6.5", ] [[package]] @@ -2271,9 +2186,9 @@ dependencies = [ [[package]] name = "nom" -version = "7.0.0" +version = "7.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffd9d26838a953b4af82cbeb9f1592c6798916983959be223a7124e992742c1" +checksum = "1b1d11e1ef389c76fe5b81bcaf2ea32cf88b62bc494e19f493d0b30e7a930109" dependencies = [ "memchr", "minimal-lexical", @@ -2401,12 +2316,6 @@ dependencies = [ "openssl-sys", ] -[[package]] -name = "openssl-probe" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a" - [[package]] name = "openssl-src" version = "300.0.2+3.0.0" @@ -2418,9 +2327,9 @@ dependencies = [ [[package]] name = "openssl-sys" -version = "0.9.70" +version = "0.9.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6517987b3f8226b5da3661dad65ff7f300cc59fb5ea8333ca191fc65fde3edf" +checksum = "7df13d165e607909b363a4757a6f133f8a818a74e9d3a98d09c6128e15fa4c73" dependencies = [ "autocfg 1.0.1", "cc", @@ -2561,12 +2470,24 @@ dependencies = [ "peach-lib", ] +[[package]] +name = "peach-jsonrpc-server" +version = "0.1.0" +dependencies = [ + "env_logger 0.9.0", + "jsonrpc-core 18.0.0", + "jsonrpc-http-server 18.0.0", + "jsonrpc-test 18.0.0", + "log 0.4.14", + "miniserde", + "peach-stats", +] + [[package]] name = "peach-lib" version = "1.3.1" dependencies = [ "chrono", - "env_logger 0.6.2", "fslock", "jsonrpc-client-core", "jsonrpc-client-http", @@ -2628,19 +2549,17 @@ dependencies = [ [[package]] name = "peach-oled" -version = "0.1.3" +version = "0.1.4" dependencies = [ "embedded-graphics", - "env_logger 0.6.2", - "jsonrpc-core 11.0.0", - "jsonrpc-http-server 11.0.0", - "jsonrpc-test 11.0.0", + "env_logger 0.9.0", + "jsonrpc-core 18.0.0", + "jsonrpc-http-server 18.0.0", + "jsonrpc-test 18.0.0", "linux-embedded-hal", "log 0.4.14", "nix 0.11.1", "serde 1.0.130", - "serde_json", - "snafu 0.4.4", "ssd1306", "tinybmp", ] @@ -2667,21 +2586,18 @@ dependencies = [ [[package]] name = "peach-stats" -version = "0.1.3" +version = "0.2.0" dependencies = [ - "env_logger 0.9.0", - "jsonrpc-core 18.0.0", - "jsonrpc-http-server 18.0.0", - "jsonrpc-test 18.0.0", "log 0.4.14", "miniserde", "probes 0.4.1", + "serde 1.0.130", "systemstat", ] [[package]] name = "peach-web" -version = "0.4.11" +version = "0.4.12" dependencies = [ "env_logger 0.8.4", "log 0.4.14", @@ -2696,7 +2612,6 @@ dependencies = [ "serde_json", "snafu 0.6.10", "tera", - "websocket", "xdg", ] @@ -2717,10 +2632,10 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "82a5ca643c2303ecb740d506539deba189e16f2754040a42901cd8105d0282d0" dependencies = [ - "proc-macro2 1.0.32", + "proc-macro2 1.0.33", "proc-macro2-diagnostics", "quote 1.0.10", - "syn 1.0.81", + "syn 1.0.82", ] [[package]] @@ -2762,9 +2677,9 @@ checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" dependencies = [ "pest", "pest_meta", - "proc-macro2 1.0.32", + "proc-macro2 1.0.33", "quote 1.0.10", - "syn 1.0.81", + "syn 1.0.82", ] [[package]] @@ -2831,9 +2746,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.22" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12295df4f294471248581bc09bef3c38a5e46f1e36d6a37353621a0c6c357e1f" +checksum = "d1a3ea4f0dd7f1f3e512cf97bf100819aa547f36a6eccac8dbaae839eb92363e" [[package]] name = "polyval" @@ -2879,9 +2794,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.32", + "proc-macro2 1.0.33", "quote 1.0.10", - "syn 1.0.81", + "syn 1.0.82", "version_check 0.9.3", ] @@ -2891,7 +2806,7 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.32", + "proc-macro2 1.0.33", "quote 1.0.10", "version_check 0.9.3", ] @@ -2902,12 +2817,6 @@ version = "0.5.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" -[[package]] -name = "proc-macro-nested" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" - [[package]] name = "proc-macro2" version = "0.4.30" @@ -2919,9 +2828,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.32" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43" +checksum = "fb37d2df5df740e582f28f8560cf425f52bb267d872fe58358eadb554909f07a" dependencies = [ "unicode-xid 0.2.2", ] @@ -2932,9 +2841,9 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4bf29726d67464d49fa6224a1d07936a8c08bb3fba727c7493f6cf1616fdaada" dependencies = [ - "proc-macro2 1.0.32", + "proc-macro2 1.0.33", "quote 1.0.10", - "syn 1.0.81", + "syn 1.0.82", "version_check 0.9.3", "yansi", ] @@ -2960,7 +2869,7 @@ version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" dependencies = [ - "proc-macro2 1.0.32", + "proc-macro2 1.0.33", ] [[package]] @@ -3222,9 +3131,9 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c38e3aecd2b21cb3959637b883bb3714bc7e43f0268b9a29d3743ee3e55cdd2" dependencies = [ - "proc-macro2 1.0.32", + "proc-macro2 1.0.33", "quote 1.0.10", - "syn 1.0.81", + "syn 1.0.82", ] [[package]] @@ -3285,7 +3194,7 @@ dependencies = [ "bytes 1.1.0", "either", "figment", - "futures 0.3.17", + "futures 0.3.18", "indexmap", "log 0.4.14", "memchr", @@ -3302,7 +3211,7 @@ dependencies = [ "state", "tempfile", "time 0.2.27", - "tokio 1.13.0", + "tokio 1.14.0", "tokio-stream", "tokio-util", "ubyte", @@ -3319,10 +3228,10 @@ dependencies = [ "devise", "glob", "indexmap", - "proc-macro2 1.0.32", + "proc-macro2 1.0.33", "quote 1.0.10", "rocket_http", - "syn 1.0.81", + "syn 1.0.82", "unicode-xid 0.2.2", ] @@ -3350,11 +3259,11 @@ dependencies = [ "cookie", "either", "http 0.2.5", - "hyper 0.14.14", + "hyper 0.14.15", "indexmap", "log 0.4.14", "memchr", - "mime 0.3.16", + "mime", "parking_lot 0.11.2", "pear", "percent-encoding 2.1.0", @@ -3365,7 +3274,7 @@ dependencies = [ "stable-pattern", "state", "time 0.2.27", - "tokio 1.13.0", + "tokio 1.14.0", "uncased", ] @@ -3412,15 +3321,6 @@ dependencies = [ "semver 0.9.0", ] -[[package]] -name = "rustc_version" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" -dependencies = [ - "semver 0.11.0", -] - [[package]] name = "rustc_version" version = "0.4.0" @@ -3432,15 +3332,15 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61b3909d758bb75c79f23d4736fac9433868679d3ad2ea7a61e3c25cfda9a088" +checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f" [[package]] name = "ryu" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +checksum = "3c9613b5a66ab9ba26415184cfc41156594925a9cf3a2057e57f31ff145f6568" [[package]] name = "safemem" @@ -3457,16 +3357,6 @@ dependencies = [ "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]] name = "scoped-tls" version = "0.1.2" @@ -3491,29 +3381,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "semver" version = "0.1.20" @@ -3526,16 +3393,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" dependencies = [ - "semver-parser 0.7.0", -] - -[[package]] -name = "semver" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" -dependencies = [ - "semver-parser 0.10.2", + "semver-parser", ] [[package]] @@ -3550,15 +3408,6 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -[[package]] -name = "semver-parser" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" -dependencies = [ - "pest", -] - [[package]] name = "serde" version = "0.8.23" @@ -3593,16 +3442,16 @@ version = "1.0.130" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" dependencies = [ - "proc-macro2 1.0.32", + "proc-macro2 1.0.33", "quote 1.0.10", - "syn 1.0.81", + "syn 1.0.82", ] [[package]] name = "serde_json" -version = "1.0.68" +version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f690853975602e1bfe1ccbf50504d67174e3bcf340f23b5ea9992e0587a52d8" +checksum = "d0ffa0837f2dfa6fb90868c2b5468cad482e175f7dad97e7421951e663f2b527" dependencies = [ "indexmap", "itoa", @@ -3787,9 +3636,9 @@ version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1508efa03c362e23817f96cde18abed596a25219a8b2c66e8db33c03543d315b" dependencies = [ - "proc-macro2 1.0.32", + "proc-macro2 1.0.33", "quote 1.0.10", - "syn 1.0.81", + "syn 1.0.82", ] [[package]] @@ -3882,11 +3731,11 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" dependencies = [ - "proc-macro2 1.0.32", + "proc-macro2 1.0.33", "quote 1.0.10", "serde 1.0.130", "serde_derive", - "syn 1.0.81", + "syn 1.0.82", ] [[package]] @@ -3896,13 +3745,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" dependencies = [ "base-x", - "proc-macro2 1.0.32", + "proc-macro2 1.0.33", "quote 1.0.10", "serde 1.0.130", "serde_derive", "serde_json", "sha1", - "syn 1.0.81", + "syn 1.0.82", ] [[package]] @@ -3945,9 +3794,9 @@ checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" dependencies = [ "heck", "proc-macro-error", - "proc-macro2 1.0.32", + "proc-macro2 1.0.33", "quote 1.0.10", - "syn 1.0.81", + "syn 1.0.82", ] [[package]] @@ -3969,11 +3818,11 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.81" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966" +checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59" dependencies = [ - "proc-macro2 1.0.32", + "proc-macro2 1.0.33", "quote 1.0.10", "unicode-xid 0.2.2", ] @@ -3984,9 +3833,9 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "proc-macro2 1.0.32", + "proc-macro2 1.0.33", "quote 1.0.10", - "syn 1.0.81", + "syn 1.0.82", "unicode-xid 0.2.2", ] @@ -4009,7 +3858,7 @@ dependencies = [ "chrono", "lazy_static", "libc", - "nom 7.0.0", + "nom 7.1.0", "winapi 0.3.9", ] @@ -4135,10 +3984,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd3c141a1b43194f3f56a1411225df8646c55781d5f26db825b3d98507eb482f" dependencies = [ "proc-macro-hack", - "proc-macro2 1.0.32", + "proc-macro2 1.0.33", "quote 1.0.10", "standback", - "syn 1.0.81", + "syn 1.0.82", ] [[package]] @@ -4161,9 +4010,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f83b2a3d4d9091d0abd7eba4dc2710b1718583bd4d8992e2190720ea38f391f7" +checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2" dependencies = [ "tinyvec_macros", ] @@ -4200,9 +4049,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.13.0" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "588b2d10a336da58d877567cd8fb8a14b463e2104910f8132cd054b4b96e29ee" +checksum = "70e992e41e0d2fb9f755b37446f20900f64446ef54874f40a60c78f021ac6144" dependencies = [ "autocfg 1.0.1", "bytes 1.1.0", @@ -4302,13 +4151,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "1.5.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "114383b041aa6212c579467afa0075fbbdd0718de036100bc0ba7961d8cb9095" +checksum = "c9efc1aba077437943f7515666aa2b882dfabfbfdf89c819ea75a8d6e9eaba5e" dependencies = [ - "proc-macro2 1.0.32", + "proc-macro2 1.0.33", "quote 1.0.10", - "syn 1.0.81", + "syn 1.0.82", ] [[package]] @@ -4365,7 +4214,7 @@ checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3" dependencies = [ "futures-core", "pin-project-lite", - "tokio 1.13.0", + "tokio 1.14.0", ] [[package]] @@ -4421,17 +4270,6 @@ dependencies = [ "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]] name = "tokio-udp" version = "0.1.6" @@ -4476,7 +4314,7 @@ dependencies = [ "futures-sink", "log 0.4.14", "pin-project-lite", - "tokio 1.13.0", + "tokio 1.14.0", ] [[package]] @@ -4513,9 +4351,9 @@ version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4f480b8f81512e825f337ad51e94c1eb5d3bbdf2b363dcd01e2b19a9ffe3f8e" dependencies = [ - "proc-macro2 1.0.32", + "proc-macro2 1.0.33", "quote 1.0.10", - "syn 1.0.81", + "syn 1.0.82", ] [[package]] @@ -4538,44 +4376,24 @@ dependencies = [ "tracing-core", ] -[[package]] -name = "tracing-serde" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb65ea441fbb84f9f6748fd496cf7f63ec9af5bca94dd86456978d055e8eb28b" -dependencies = [ - "serde 1.0.130", - "tracing-core", -] - [[package]] name = "tracing-subscriber" -version = "0.2.25" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71" +checksum = "245da694cc7fc4729f3f418b304cb57789f1bed2a78c575407ab8a23f53cb4d3" dependencies = [ - "ansi_term 0.12.1", - "chrono", + "ansi_term", "lazy_static", "matchers", "regex", - "serde 1.0.130", - "serde_json", "sharded-slab", "smallvec 1.7.0", "thread_local", "tracing", "tracing-core", "tracing-log", - "tracing-serde", ] -[[package]] -name = "traitobject" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" - [[package]] name = "try-lock" version = "0.1.0" @@ -4598,12 +4416,6 @@ dependencies = [ "unchecked-index", ] -[[package]] -name = "typeable" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887" - [[package]] name = "typenum" version = "1.14.0" @@ -4691,15 +4503,6 @@ dependencies = [ "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]] name = "unicase" version = "2.6.0" @@ -4873,9 +4676,9 @@ dependencies = [ "bumpalo", "lazy_static", "log 0.4.14", - "proc-macro2 1.0.32", + "proc-macro2 1.0.33", "quote 1.0.10", - "syn 1.0.81", + "syn 1.0.82", "wasm-bindgen-shared", ] @@ -4895,9 +4698,9 @@ version = "0.2.78" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" dependencies = [ - "proc-macro2 1.0.32", + "proc-macro2 1.0.33", "quote 1.0.10", - "syn 1.0.81", + "syn 1.0.82", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4908,47 +4711,6 @@ version = "0.2.78" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "winapi" version = "0.2.8" diff --git a/Cargo.toml b/Cargo.toml index dd1560b..1ccc7d9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ members = [ "peach-menu", "peach-monitor", "peach-stats", + "peach-jsonrpc-server", "peach-probe", "peach-dyndns-updater" ] diff --git a/peach-jsonrpc-server/Cargo.toml b/peach-jsonrpc-server/Cargo.toml new file mode 100644 index 0000000..7011cbb --- /dev/null +++ b/peach-jsonrpc-server/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "peach-jsonrpc-server" +authors = ["Andrew Reid "] +version = "0.1.0" +edition = "2021" +description = "JSON-RPC over HTTP for the PeachCloud system. Provides a JSON-RPC wrapper around the stats, network and oled libraries." +homepage = "https://opencollective.com/peachcloud" +repository = "https://git.coopcloud.tech/PeachCloud/peach-workspace" +readme = "README.md" +license = "AGPL-3.0-only" +publish = false + +[badges] +maintenance = { status = "actively-developed" } + +[dependencies] +env_logger = "0.9" +jsonrpc-core = "18" +jsonrpc-http-server = "18" +log = "0.4" +miniserde = "0.1.15" +peach-stats = { path = "../peach-stats", features = ["miniserde_support"] } + +[dev-dependencies] +jsonrpc-test = "18" diff --git a/peach-jsonrpc-server/README.md b/peach-jsonrpc-server/README.md new file mode 100644 index 0000000..e13a0eb --- /dev/null +++ b/peach-jsonrpc-server/README.md @@ -0,0 +1,72 @@ +# peach-jsonrpc-server + +A JSON-RPC server for the PeachCloud system which exposes an API over HTTP. + +Currently includes peach-stats capability (system statistics). + +## JSON-RPC API + +| Method | Description | Returns | +| --- | --- | --- | +| `cpu_stats` | CPU statistics | `user`, `system`, `nice`, `idle` | +| `cpu_stats_percent` | CPU statistics as percentages | `user`, `system`, `nice`, `idle` | +| `disk_usage` | Disk usage statistics (array of disks) | `filesystem`, `one_k_blocks`, `one_k_blocks_used`, `one_k_blocks_free`, `used_percentage`, `mountpoint` | +| `load_average` | Load average statistics | `one`, `five`, `fifteen` | +| `mem_stats` | Memory statistics | `total`, `free`, `used` | +| `ping` | Microservice status | `success` if running | +| `uptime` | System uptime | `secs` | + +## Environment + +The JSON-RPC HTTP server is currently hardcoded to run on "127.0.0.1:5110". Address and port configuration settings will later be exposed via CLI arguments and possibly an environment variable. + +Logging is made available with `env_logger`: + +`export RUST_LOG=info` + +Other logging levels include `debug`, `warn` and `error`. + +## Setup + +Clone the peach-workspace repo: + +`git clone https://git.coopcloud.tech/PeachCloud/peach-workspace` + +Move into the repo peaach-jsonrpc-server directory and compile a release build: + +`cd peach-jsonrpc-server` +`cargo build --release` + +Run the binary: + +`./peach-workspace/target/release/peach-jsonrpc-server` + +## Debian Packaging + +TODO. + +## Example Usage + +**Get CPU Statistics** + +With microservice running, open a second terminal window and use `curl` to call server methods: + +`curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0", "method": "cpu_stats", "id":1 }' 127.0.0.1:5110` + +Server responds with: + +`{"jsonrpc":"2.0","result":"{\"user\":4661083,\"system\":1240371,\"idle\":326838290,\"nice\":0}","id":1}` + +**Get System Uptime** + +With microservice running, open a second terminal window and use `curl` to call server methods: + +`curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0", "method": "uptime", "id":1 }' 127.0.0.1:5110` + +Server responds with: + +`{"jsonrpc":"2.0","result":"{\"secs\":840968}","id":1}` + +### Licensing + +AGPL-3.0 diff --git a/peach-jsonrpc-server/src/error.rs b/peach-jsonrpc-server/src/error.rs new file mode 100644 index 0000000..9e14dbf --- /dev/null +++ b/peach-jsonrpc-server/src/error.rs @@ -0,0 +1,46 @@ +use std::fmt; + +use jsonrpc_core::{Error as JsonRpcError, ErrorCode}; +use peach_stats::StatsError; + +/// Custom error type encapsulating all possible errors for a JSON-RPC server +/// and associated methods. +#[derive(Debug)] +pub enum JsonRpcServerError { + /// An error returned from the `peach-stats` library. + Stats(StatsError), + /// An expected JSON-RPC method parameter was not provided. + MissingParameter(JsonRpcError), + /// Failed to parse a provided JSON-RPC method parameter. + ParseParameter(JsonRpcError), +} + +impl fmt::Display for JsonRpcServerError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + JsonRpcServerError::ParseParameter(ref source) => { + write!(f, "Failed to parse parameter: {}", source) + } + JsonRpcServerError::MissingParameter(ref source) => { + write!(f, "Missing expected parameter: {}", source) + } + JsonRpcServerError::Stats(ref source) => { + write!(f, "{}", source) + } + } + } +} + +impl From for JsonRpcError { + fn from(err: JsonRpcServerError) -> Self { + match &err { + JsonRpcServerError::Stats(source) => JsonRpcError { + code: ErrorCode::ServerError(-32001), + message: format!("{}", source), + data: None, + }, + JsonRpcServerError::MissingParameter(source) => source.clone(), + JsonRpcServerError::ParseParameter(source) => source.clone(), + } + } +} diff --git a/peach-jsonrpc-server/src/lib.rs b/peach-jsonrpc-server/src/lib.rs new file mode 100644 index 0000000..43ecbf7 --- /dev/null +++ b/peach-jsonrpc-server/src/lib.rs @@ -0,0 +1,141 @@ +//! # peach-jsonrpc-server +//! +//! A JSON-RPC server which exposes an API over HTTP. + +use std::env; +use std::result::Result; + +use jsonrpc_core::{IoHandler, Value}; +use jsonrpc_http_server::{AccessControlAllowOrigin, DomainsValidation, ServerBuilder}; +use log::info; +use miniserde::json; +use peach_stats::stats; + +mod error; +use crate::error::JsonRpcServerError; + +/// Create JSON-RPC I/O handler, add RPC methods and launch HTTP server. +pub fn run() -> Result<(), JsonRpcServerError> { + info!("Starting up."); + + info!("Creating JSON-RPC I/O handler."); + let mut io = IoHandler::default(); + + io.add_sync_method("ping", |_| Ok(Value::String("success".to_string()))); + + // TODO: add blocks of methods according to provided flags + + /* PEACH-STATS RPC METHODS */ + + io.add_sync_method("cpu_stats", move |_| { + info!("Fetching CPU statistics."); + let cpu = stats::cpu_stats().map_err(JsonRpcServerError::Stats)?; + let json_cpu = json::to_string(&cpu); + + Ok(Value::String(json_cpu)) + }); + + io.add_sync_method("cpu_stats_percent", move |_| { + info!("Fetching CPU statistics as percentages."); + let cpu = stats::cpu_stats_percent().map_err(JsonRpcServerError::Stats)?; + let json_cpu = json::to_string(&cpu); + + Ok(Value::String(json_cpu)) + }); + + io.add_sync_method("disk_usage", move |_| { + info!("Fetching disk usage statistics."); + let disks = stats::disk_usage().map_err(JsonRpcServerError::Stats)?; + let json_disks = json::to_string(&disks); + + Ok(Value::String(json_disks)) + }); + + io.add_sync_method("load_average", move |_| { + info!("Fetching system load average statistics."); + let avg = stats::load_average().map_err(JsonRpcServerError::Stats)?; + let json_avg = json::to_string(&avg); + + Ok(Value::String(json_avg)) + }); + + io.add_sync_method("mem_stats", move |_| { + info!("Fetching current memory statistics."); + let mem = stats::mem_stats().map_err(JsonRpcServerError::Stats)?; + let json_mem = json::to_string(&mem); + + Ok(Value::String(json_mem)) + }); + + io.add_sync_method("uptime", move |_| { + info!("Fetching system uptime."); + let uptime = stats::uptime().map_err(JsonRpcServerError::Stats)?; + let json_uptime = json::to_string(&uptime); + + Ok(Value::String(json_uptime)) + }); + + let http_server = + env::var("PEACH_JSONRPC_SERVER").unwrap_or_else(|_| "127.0.0.1:5110".to_string()); + + info!("Starting JSON-RPC server on {}.", http_server); + let server = ServerBuilder::new(io) + .cors(DomainsValidation::AllowOnly(vec![ + AccessControlAllowOrigin::Null, + ])) + .start_http( + &http_server + .parse() + .expect("Invalid HTTP address and port combination"), + ) + .expect("Unable to start RPC server"); + + server.wait(); + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + use jsonrpc_core::{Error as JsonRpcError, ErrorCode}; + use jsonrpc_test as test_rpc; + + #[test] + fn rpc_success() { + let rpc = { + let mut io = IoHandler::new(); + io.add_sync_method("rpc_success_response", |_| { + Ok(Value::String("success".into())) + }); + test_rpc::Rpc::from(io) + }; + + assert_eq!(rpc.request("rpc_success_response", &()), r#""success""#); + } + + #[test] + fn rpc_parse_error() { + let rpc = { + let mut io = IoHandler::new(); + io.add_sync_method("rpc_parse_error", |_| { + let e = JsonRpcError { + code: ErrorCode::ParseError, + message: String::from("Parse error"), + data: None, + }; + Err(JsonRpcError::from(JsonRpcServerError::MissingParameter(e))) + }); + test_rpc::Rpc::from(io) + }; + + assert_eq!( + rpc.request("rpc_parse_error", &()), + r#"{ + "code": -32700, + "message": "Parse error" +}"# + ); + } +} diff --git a/peach-jsonrpc-server/src/main.rs b/peach-jsonrpc-server/src/main.rs new file mode 100644 index 0000000..cfbf2c6 --- /dev/null +++ b/peach-jsonrpc-server/src/main.rs @@ -0,0 +1,34 @@ +#![warn(missing_docs)] + +//! # peach-jsonrpc-server +//! +//! A JSON-RPC server which exposes an over HTTP. +//! +//! Currently includes peach-stats capability (system statistics). +//! +//! ## API +//! +//! | Method | Description | Returns | +//! | --- | --- | --- | +//! | `cpu_stats` | CPU statistics | `user`, `system`, `nice`, `idle` | +//! | `cpu_stats_percent` | CPU statistics as percentages | `user`, `system`, `nice`, `idle` | +//! | `disk_usage` | Disk usage statistics (array of disks) | `filesystem`, `one_k_blocks`, `one_k_blocks_used`, `one_k_blocks_free`, `used_percentage`, `mountpoint` | +//! | `load_average` | Load average statistics | `one`, `five`, `fifteen` | +//! | `mem_stats` | Memory statistics | `total`, `free`, `used` | +//! | `ping` | Microservice status | `success` if running | +//! | `uptime` | System uptime | `secs` | + +use std::process; + +use log::error; + +fn main() { + // initalize the logger + env_logger::init(); + + // handle errors returned from `run` + if let Err(e) = peach_jsonrpc_server::run() { + error!("Application error: {}", e); + process::exit(1); + } +} diff --git a/peach-lib/Cargo.toml b/peach-lib/Cargo.toml index 31f8960..8085f69 100644 --- a/peach-lib/Cargo.toml +++ b/peach-lib/Cargo.toml @@ -6,7 +6,6 @@ edition = "2018" [dependencies] chrono = "0.4.19" -env_logger = "0.6" fslock="0.1.6" jsonrpc-client-core = "0.5" jsonrpc-client-http = "0.5" diff --git a/peach-oled/Cargo.toml b/peach-oled/Cargo.toml index 680e267..3da3807 100644 --- a/peach-oled/Cargo.toml +++ b/peach-oled/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "peach-oled" -version = "0.1.3" +version = "0.1.4" authors = ["Andrew Reid "] edition = "2018" description = "Write and draw to OLED display using JSON-RPC over HTTP." @@ -27,18 +27,16 @@ travis-ci = { repository = "peachcloud/peach-oled", branch = "master" } maintenance = { status = "actively-developed" } [dependencies] -jsonrpc-core = "11.0.0" -jsonrpc-http-server = "11.0.0" -linux-embedded-hal = "0.2.2" embedded-graphics = "0.4.7" -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" +env_logger = "0.9" +jsonrpc-core = "18" +jsonrpc-http-server = "18" +linux-embedded-hal = "0.2.2" +log = "0.4" +serde = { version = "1", features = ["derive"] } nix="0.11" +ssd1306 = "0.2.6" +tinybmp = "0.1.0" [dev-dependencies] -jsonrpc-test = "11.0.0" +jsonrpc-test = "18" diff --git a/peach-oled/README.md b/peach-oled/README.md index 9829aea..b41aade 100644 --- a/peach-oled/README.md +++ b/peach-oled/README.md @@ -1,6 +1,6 @@ # peach-oled -[![Build Status](https://travis-ci.com/peachcloud/peach-oled.svg?branch=master)](https://travis-ci.com/peachcloud/peach-oled) ![Generic badge](https://img.shields.io/badge/version-0.1.3-.svg) +[![Build Status](https://travis-ci.com/peachcloud/peach-oled.svg?branch=master)](https://travis-ci.com/peachcloud/peach-oled) ![Generic badge](https://img.shields.io/badge/version-0.1.4-.svg) 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. diff --git a/peach-oled/src/error.rs b/peach-oled/src/error.rs index 1926d54..3f5264c 100644 --- a/peach-oled/src/error.rs +++ b/peach-oled/src/error.rs @@ -1,44 +1,68 @@ -use std::error; +use std::{error, fmt}; -use jsonrpc_core::{types::error::Error, ErrorCode}; -use linux_embedded_hal as hal; -use snafu::Snafu; +use jsonrpc_core::types::error::Error as JsonRpcError; +use jsonrpc_core::ErrorCode; +use linux_embedded_hal::i2cdev::linux::LinuxI2CError; -pub type BoxError = Box; - -#[derive(Debug, Snafu)] -#[snafu(visibility(pub(crate)))] +#[derive(Debug)] pub enum OledError { - #[snafu(display("Failed to create interface for I2C device: {}", source))] I2CError { - source: hal::i2cdev::linux::LinuxI2CError, + source: LinuxI2CError, }, - - #[snafu(display("Coordinate {} out of range {}: {}", coord, range, value))] InvalidCoordinate { coord: String, range: String, value: i32, }, - - // TODO: implement for validate() in src/lib.rs - #[snafu(display("Font size invalid: {}", font))] - InvalidFontSize { font: String }, - - #[snafu(display("String length out of range 0-21: {}", len))] - InvalidString { len: usize }, - - #[snafu(display("Missing expected parameter: {}", e))] - MissingParameter { e: Error }, - - #[snafu(display("Failed to parse parameter: {}", e))] - ParseError { e: Error }, + InvalidFontSize { + font: String, + }, + InvalidString { + len: usize, + }, + MissingParameter { + source: JsonRpcError, + }, + ParseError { + source: JsonRpcError, + }, } -impl From for Error { +impl error::Error for OledError {} + +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 for JsonRpcError { fn from(err: OledError) -> Self { match &err { - OledError::I2CError { source } => Error { + OledError::I2CError { source } => JsonRpcError { code: ErrorCode::ServerError(-32000), message: format!("Failed to create interface for I2C device: {}", source), data: None, @@ -47,7 +71,7 @@ impl From for Error { coord, value, range, - } => Error { + } => JsonRpcError { code: ErrorCode::ServerError(-32001), message: format!( "Validation error: coordinate {} out of range {}: {}", @@ -55,18 +79,18 @@ impl From for Error { ), data: None, }, - OledError::InvalidFontSize { font } => Error { + OledError::InvalidFontSize { font } => JsonRpcError { code: ErrorCode::ServerError(-32002), message: format!("Validation error: {} is not an accepted font size. Use 6x8, 6x12, 8x16 or 12x16 instead", font), data: None, }, - OledError::InvalidString { len } => Error { + OledError::InvalidString { len } => JsonRpcError { code: ErrorCode::ServerError(-32003), message: format!("Validation error: string length {} out of range 0-21", len), data: None, }, - OledError::MissingParameter { e } => e.clone(), - OledError::ParseError { e } => e.clone(), + OledError::MissingParameter { source } => source.clone(), + OledError::ParseError { source } => source.clone(), } } } diff --git a/peach-oled/src/lib.rs b/peach-oled/src/lib.rs index d66dc5f..eb012e6 100644 --- a/peach-oled/src/lib.rs +++ b/peach-oled/src/lib.rs @@ -6,23 +6,23 @@ use std::{ sync::{Arc, Mutex}, }; -use embedded_graphics::coord::Coord; -use embedded_graphics::fonts::{Font12x16, Font6x12, Font6x8, Font8x16}; -use embedded_graphics::image::Image1BPP; -use embedded_graphics::prelude::*; +use embedded_graphics::{ + coord::Coord, + fonts::{Font12x16, Font6x12, Font6x8, Font8x16}, + image::Image1BPP, + prelude::*, +}; use hal::I2cdev; use jsonrpc_core::{types::error::Error, IoHandler, Params, Value}; use jsonrpc_http_server::{AccessControlAllowOrigin, DomainsValidation, ServerBuilder}; use linux_embedded_hal as hal; use log::{debug, error, info}; use serde::Deserialize; -use snafu::{ensure, ResultExt}; -use ssd1306::prelude::*; -use ssd1306::Builder; +use ssd1306::{prelude::*, Builder}; -use crate::error::{BoxError, I2CError, InvalidCoordinate, InvalidString, OledError}; +use crate::error::OledError; -//define the Graphic struct for receiving draw commands +// define the Graphic struct for receiving draw commands #[derive(Debug, Deserialize)] pub struct Graphic { bytes: Vec, @@ -32,7 +32,7 @@ pub struct Graphic { y_coord: i32, } -//define the Msg struct for receiving write commands +// define the Msg struct for receiving write commands #[derive(Debug, Deserialize)] pub struct Msg { x_coord: i32, @@ -41,86 +41,61 @@ pub struct Msg { 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)] pub struct On { on: bool, } -fn validate(m: &Msg) -> Result<(), OledError> { - ensure!( - m.string.len() <= 21, - InvalidString { - len: m.string.len() - } - ); - - ensure!( - m.x_coord >= 0, - InvalidCoordinate { +fn validate(msg: &Msg) -> Result<(), OledError> { + if msg.string.len() > 21 { + Err(OledError::InvalidString { + len: msg.string.len(), + }) + } else if msg.x_coord < 0 || msg.x_coord > 128 { + Err(OledError::InvalidCoordinate { coord: "x".to_string(), range: "0-128".to_string(), - value: m.x_coord, - } - ); - - 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 { + value: msg.x_coord, + }) + } else if msg.y_coord < 0 || msg.y_coord > 147 { + Err(OledError::InvalidCoordinate { coord: "y".to_string(), range: "0-47".to_string(), - value: m.y_coord, - } - ); - - ensure!( - m.y_coord < 148, - InvalidCoordinate { - coord: "y".to_string(), - range: "0-47".to_string(), - value: m.y_coord, - } - ); - - Ok(()) + value: msg.y_coord, + }) + } else { + Ok(()) + } } -pub fn run() -> Result<(), BoxError> { +pub fn run() -> Result<(), OledError> { info!("Starting up."); debug!("Creating interface for I2C device."); - let i2c = I2cdev::new("/dev/i2c-1").context(I2CError)?; + let i2c = I2cdev::new("/dev/i2c-1").map_err(|source| OledError::I2CError { source })?; - let mut disp: GraphicsMode<_> = Builder::new().connect_i2c(i2c).into(); + let mut display: GraphicsMode<_> = Builder::new().connect_i2c(i2c).into(); info!("Initializing the display."); - disp.init().unwrap_or_else(|_| { + display.init().unwrap_or_else(|_| { error!("Problem initializing the OLED display."); process::exit(1); }); debug!("Flushing the display."); - disp.flush().unwrap_or_else(|_| { + display.flush().unwrap_or_else(|_| { error!("Problem flushing the OLED display."); process::exit(1); }); - let oled = Arc::new(Mutex::new(disp)); + let oled = Arc::new(Mutex::new(display)); let oled_clone = Arc::clone(&oled); info!("Creating JSON-RPC I/O handler."); let mut io = IoHandler::default(); - io.add_method("clear", move |_| { + io.add_sync_method("clear", move |_| { let mut oled = oled_clone.lock().unwrap(); info!("Clearing the display."); oled.clear(); @@ -134,21 +109,20 @@ pub fn run() -> Result<(), BoxError> { let oled_clone = Arc::clone(&oled); - io.add_method("draw", move |params: Params| { - let g: Result = params.parse(); - let g: Graphic = g?; + io.add_sync_method("draw", move |params: Params| { + let graphic: Graphic = params.parse()?; // TODO: add simple byte validation function let mut oled = oled_clone.lock().unwrap(); info!("Drawing image to the display."); - let im = - Image1BPP::new(&g.bytes, g.width, g.height).translate(Coord::new(g.x_coord, g.y_coord)); - oled.draw(im.into_iter()); + let image = Image1BPP::new(&graphic.bytes, graphic.width, graphic.height) + .translate(Coord::new(graphic.x_coord, graphic.y_coord)); + oled.draw(image.into_iter()); Ok(Value::String("success".into())) }); let oled_clone = Arc::clone(&oled); - io.add_method("flush", move |_| { + io.add_sync_method("flush", move |_| { let mut oled = oled_clone.lock().unwrap(); info!("Flushing the display."); oled.flush().unwrap_or_else(|_| { @@ -160,9 +134,9 @@ pub fn run() -> Result<(), BoxError> { let oled_clone = Arc::clone(&oled); - io.add_method("ping", |_| Ok(Value::String("success".to_string()))); + io.add_sync_method("ping", |_| Ok(Value::String("success".to_string()))); - io.add_method("power", move |params: Params| { + io.add_sync_method("power", move |params: Params| { let o: Result = params.parse(); let o: On = o?; let mut oled = oled_clone.lock().unwrap(); @@ -180,37 +154,36 @@ pub fn run() -> Result<(), BoxError> { let oled_clone = Arc::clone(&oled); - io.add_method("write", move |params: Params| { + io.add_sync_method("write", move |params: Params| { info!("Received a 'write' request."); - let m: Result = params.parse(); - let m: Msg = m?; - validate(&m)?; + let msg = params.parse()?; + validate(&msg)?; let mut oled = oled_clone.lock().unwrap(); info!("Writing to the display."); - if m.font_size == "6x8" { + if msg.font_size == "6x8" { oled.draw( - Font6x8::render_str(&m.string) - .translate(Coord::new(m.x_coord, m.y_coord)) + Font6x8::render_str(&msg.string) + .translate(Coord::new(msg.x_coord, msg.y_coord)) .into_iter(), ); - } else if m.font_size == "6x12" { + } else if msg.font_size == "6x12" { oled.draw( - Font6x12::render_str(&m.string) - .translate(Coord::new(m.x_coord, m.y_coord)) + Font6x12::render_str(&msg.string) + .translate(Coord::new(msg.x_coord, msg.y_coord)) .into_iter(), ); - } else if m.font_size == "8x16" { + } else if msg.font_size == "8x16" { oled.draw( - Font8x16::render_str(&m.string) - .translate(Coord::new(m.x_coord, m.y_coord)) + Font8x16::render_str(&msg.string) + .translate(Coord::new(msg.x_coord, msg.y_coord)) .into_iter(), ); - } else if m.font_size == "12x16" { + } else if msg.font_size == "12x16" { oled.draw( - Font12x16::render_str(&m.string) - .translate(Coord::new(m.x_coord, m.y_coord)) + Font12x16::render_str(&msg.string) + .translate(Coord::new(msg.x_coord, msg.y_coord)) .into_iter(), ); } @@ -255,7 +228,7 @@ mod tests { fn rpc_success() { let rpc = { let mut io = IoHandler::new(); - io.add_method("rpc_success_response", |_| { + io.add_sync_method("rpc_success_response", |_| { Ok(Value::String("success".into())) }); test_rpc::Rpc::from(io) @@ -269,7 +242,7 @@ mod tests { fn rpc_internal_error() { let rpc = { let mut io = IoHandler::new(); - io.add_method("rpc_internal_error", |_| Err(Error::internal_error())); + io.add_sync_method("rpc_internal_error", |_| Err(Error::internal_error())); test_rpc::Rpc::from(io) }; @@ -287,7 +260,7 @@ mod tests { fn rpc_i2c_io_error() { let rpc = { let mut io = IoHandler::new(); - io.add_method("rpc_i2c_io_error", |_| { + io.add_sync_method("rpc_i2c_io_error", |_| { let io_err = IoError::new(ErrorKind::PermissionDenied, "oh no!"); let source = LinuxI2CError::Io(io_err); Err(Error::from(OledError::I2CError { source })) @@ -310,7 +283,7 @@ mod tests { fn rpc_i2c_nix_error() { let rpc = { let mut io = IoHandler::new(); - io.add_method("rpc_i2c_nix_error", |_| { + io.add_sync_method("rpc_i2c_nix_error", |_| { let nix_err = NixError::InvalidPath; let source = LinuxI2CError::Nix(nix_err); Err(Error::from(OledError::I2CError { source })) @@ -326,14 +299,14 @@ mod tests { }"# ); } - */ + */ // test to ensure correct InvalidCoordinate error response #[test] fn rpc_invalid_coord() { let rpc = { let mut io = IoHandler::new(); - io.add_method("rpc_invalid_coord", |_| { + io.add_sync_method("rpc_invalid_coord", |_| { Err(Error::from(OledError::InvalidCoordinate { coord: "x".to_string(), range: "0-128".to_string(), @@ -357,7 +330,7 @@ mod tests { fn rpc_invalid_fontsize() { let rpc = { let mut io = IoHandler::new(); - io.add_method("rpc_invalid_fontsize", |_| { + io.add_sync_method("rpc_invalid_fontsize", |_| { Err(Error::from(OledError::InvalidFontSize { font: "24x32".to_string(), })) @@ -379,7 +352,7 @@ mod tests { fn rpc_invalid_string() { let rpc = { let mut io = IoHandler::new(); - io.add_method("rpc_invalid_string", |_| { + io.add_sync_method("rpc_invalid_string", |_| { Err(Error::from(OledError::InvalidString { len: 22 })) }); test_rpc::Rpc::from(io) @@ -399,15 +372,15 @@ mod tests { fn rpc_invalid_params() { let rpc = { let mut io = IoHandler::new(); - io.add_method("rpc_invalid_params", |_| { - let e = Error { + io.add_sync_method("rpc_invalid_params", |_| { + let source = Error { code: ErrorCode::InvalidParams, message: String::from("invalid params"), data: Some(Value::String( "Invalid params: invalid type: null, expected struct Msg.".into(), )), }; - Err(Error::from(OledError::MissingParameter { e })) + Err(Error::from(OledError::MissingParameter { source })) }); test_rpc::Rpc::from(io) }; @@ -427,13 +400,13 @@ mod tests { fn rpc_parse_error() { let rpc = { let mut io = IoHandler::new(); - io.add_method("rpc_parse_error", |_| { - let e = Error { + io.add_sync_method("rpc_parse_error", |_| { + let source = Error { code: ErrorCode::ParseError, message: String::from("Parse error"), data: None, }; - Err(Error::from(OledError::ParseError { e })) + Err(Error::from(OledError::ParseError { source })) }); test_rpc::Rpc::from(io) }; diff --git a/peach-stats/Cargo.toml b/peach-stats/Cargo.toml index c8a4914..488d637 100644 --- a/peach-stats/Cargo.toml +++ b/peach-stats/Cargo.toml @@ -1,40 +1,31 @@ [package] name = "peach-stats" -version = "0.1.3" -authors = ["Andrew Reid "] +version = "0.2.0" +authors = ["Andrew Reid "] edition = "2018" -description = "Query system statistics using JSON-RPC over HTTP. Provides a JSON-RPC wrapper around the probes and systemstat crates." +description = "Query system statistics. Provides a wrapper around the probes and systemstat crates." +keywords = ["peachcloud", "system stats", "system statistics", "disk", "memory"] homepage = "https://opencollective.com/peachcloud" -repository = "https://github.com/peachcloud/peach-stats" +repository = "https://git.coopcloud.tech/PeachCloud/peach-workspace/src/branch/main/peach-stats" readme = "README.md" -license = "AGPL-3.0-only" +license = "LGPL-3.0-only" publish = false -[package.metadata.deb] -depends = "$auto" -extended-description = """\ -peach-stats is a system statistics microservice module for PeachCloud. \ -Query system statistics using JSON-RPC over HTTP. Provides a JSON-RPC \ -wrapper around the probes and systemstat crates.""" -maintainer-scripts="debian" -systemd-units = { unit-name = "peach-stats" } -assets = [ - ["target/release/peach-stats", "usr/bin/", "755"], - ["README.md", "usr/share/doc/peach-stats/README", "644"], -] - [badges] -travis-ci = { repository = "peachcloud/peach-stats", branch = "master" } maintenance = { status = "actively-developed" } [dependencies] -env_logger = "0.9" -jsonrpc-core = "18" -jsonrpc-http-server = "18" log = "0.4" -miniserde = "0.1.15" +miniserde = { version = "0.1.15", optional = true } probes = "0.4.1" +serde = { version = "1.0.130", features = ["derive"], optional = true } systemstat = "0.1.10" -[dev-dependencies] -jsonrpc-test = "18" +[features] +default = [] + +# Provide `Serialize` and `Deserialize` traits for library structs using `miniserde` +miniserde_support = ["miniserde"] + +# Provide `Serialize` and `Deserialize` traits for library structs using `serde` +serde_support = ["serde"] diff --git a/peach-stats/README.md b/peach-stats/README.md index b2d527e..1245532 100644 --- a/peach-stats/README.md +++ b/peach-stats/README.md @@ -1,109 +1,47 @@ # peach-stats -[![Build Status](https://travis-ci.com/peachcloud/peach-stats.svg?branch=master)](https://travis-ci.com/peachcloud/peach-stats) ![Generic badge](https://img.shields.io/badge/version-0.1.3-.svg) +![Generic badge](https://img.shields.io/badge/version-0.2.0-.svg) -System statistics microservice module for PeachCloud. Provides a JSON-RPC wrapper around the [probes](https://crates.io/crates/probes) and [systemstat](https://crates.io/crates/systemstat) crates. +System statistics library for PeachCloud. Provides a wrapper around the [probes](https://crates.io/crates/probes) and [systemstat](https://crates.io/crates/systemstat) crates. -### JSON-RPC API +Currently offers the following statistics and associated data structures: -| Method | Description | Returns | -| --- | --- | --- | -| `cpu_stats` | CPU statistics | `user`, `system`, `nice`, `idle` | -| `cpu_stats_percent` | CPU statistics as percentages | `user`, `system`, `nice`, `idle` | -| `disk_usage` | Disk usage statistics (array of disks) | `filesystem`, `one_k_blocks`, `one_k_blocks_used`, `one_k_blocks_free`, `used_percentage`, `mountpoint` | -| `load_average` | Load average statistics | `one`, `five`, `fifteen` | -| `mem_stats` | Memory statistics | `total`, `free`, `used` | -| `ping` | Microservice status | `success` if running | -| `uptime` | System uptime | `secs` | + - CPU: `user`, `system`, `nice`, `idle` (as values or percentages) + - Disk usage: `filesystem`, `one_k_blocks`, `one_k_blocks_used`, + `one_k_blocks_free`, `used_percentage`, `mountpoint` + - Load average: `one`, `five`, `fifteen` + - Memory: `total`, `free`, `used` + - Uptime: `seconds` -### Environment +## Example Usage -The JSON-RPC HTTP server address and port can be configured with the `PEACH_STATS_SERVER` environment variable: +```rust +use peach_stats::{stats, StatsError}; -`export PEACH_STATS_SERVER=127.0.0.1:5000` +fn main() -> Result<(), StatsError> { + let cpu = stats::cpu_stats()?; + let cpu_percentages = stats::cpu_stats_percent()?; + let disks = stats::disk_usage()?; + let load = stats::load_average()?; + let mem = stats::mem_stats()?; + let uptime = stats::uptime()?; -When not set, the value defaults to `127.0.0.1:5113`. + // do things with the retrieved values... -Logging is made available with `env_logger`: + Ok(()) +} +``` -`export RUST_LOG=info` +## Feature Flags -Other logging levels include `debug`, `warn` and `error`. +Feature flags are used to offer `Serialize` and `Deserialize` implementations for all `struct` data types provided by this library. These traits are not provided by default. A choice of `miniserde` and `serde` is provided. -### Setup +Define the desired feature in the `Cargo.toml` manifest of your project: -Clone this repo: +```toml +peach-stats = { version = "0.1.0", features = ["miniserde_support"] } +``` -`git clone https://github.com/peachcloud/peach-stats.git` - -Move into the repo and compile a release build: - -`cd peach-stats` -`cargo build --release` - -Run the binary: - -`./target/release/peach-stats` - -### Debian Packaging - -A `systemd` service file and Debian maintainer scripts are included in the `debian` directory, allowing `peach-stats` to be easily bundled as a Debian package (`.deb`). The `cargo-deb` [crate](https://crates.io/crates/cargo-deb) can be used to achieve this. - -Install `cargo-deb`: - -`cargo install cargo-deb` - -Move into the repo: - -`cd peach-stats` - -Build the package: - -`cargo deb` - -The output will be written to `target/debian/peach-stats_0.1.0_arm64.deb` (or similar). - -Build the package (aarch64): - -`cargo deb --target aarch64-unknown-linux-gnu` - -Install the package as follows: - -`sudo dpkg -i target/debian/peach-stats_0.1.0_arm64.deb` - -The service will be automatically enabled and started. - -Uninstall the service: - -`sudo apt-get remove peach-stats` - -Remove configuration files (not removed with `apt-get remove`): - -`sudo apt-get purge peach-stats` - -### Example Usage - -**Get CPU Statistics** - -With microservice running, open a second terminal window and use `curl` to call server methods: - -`curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0", "method": "cpu_stats", "id":1 }' 127.0.0.1:5113` - -Server responds with: - -`{"jsonrpc":"2.0","result":"{\"user\":4661083,\"system\":1240371,\"idle\":326838290,\"nice\":0}","id":1}` - -**Get System Uptime** - -With microservice running, open a second terminal window and use `curl` to call server methods: - -`curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0", "method": "uptime", "id":1 }' 127.0.0.1:5113` - -Server responds with: - -`{"jsonrpc":"2.0","result":"{\"secs\":840968}","id":1}` - -### Licensing - -AGPL-3.0 +## License +LGPL-3.0. diff --git a/peach-stats/debian/peach-stats.service b/peach-stats/debian/peach-stats.service deleted file mode 100644 index a32c506..0000000 --- a/peach-stats/debian/peach-stats.service +++ /dev/null @@ -1,27 +0,0 @@ -[Unit] -Description=Query system statistics using JSON-RPC over HTTP. - -[Service] -Type=simple -User=peach-stats -Environment="RUST_LOG=error" -ExecStart=/usr/bin/peach-stats -Restart=always -CapabilityBoundingSet=~CAP_SYS_ADMIN CAP_SYS_PTRACE CAP_SYS_BOOT CAP_SYS_TIME CAP_KILL CAP_WAKE_ALARM CAP_LINUX_IMMUTABLE CAP_BLOCK_SUSPEND CAP_LEASE CAP_SYS_NICE CAP_SYS_RESOURCE CAP_RAWIO CAP_CHOWN CAP_FSETID CAP_SETFCAP CAP_DAC_* CAP_FOWNER CAP_IPC_OWNER CAP_SETUID CAP_SETGID CAP_SETPCAP CAP_AUDIT_* -InaccessibleDirectories=/home -LockPersonality=yes -NoNewPrivileges=yes -PrivateDevices=yes -PrivateTmp=yes -PrivateUsers=yes -ProtectControlGroups=yes -ProtectHome=yes -ProtectKernelModules=yes -ProtectKernelTunables=yes -ProtectSystem=yes -ReadOnlyDirectories=/var -RestrictAddressFamilies=~AF_INET6 AF_UNIX -SystemCallFilter=~@reboot @clock @debug @module @mount @swap @resources @privileged - -[Install] -WantedBy=multi-user.target diff --git a/peach-stats/src/error.rs b/peach-stats/src/error.rs index b558364..d782e35 100644 --- a/peach-stats/src/error.rs +++ b/peach-stats/src/error.rs @@ -1,69 +1,44 @@ -use std::{error, fmt, io}; +//! Custom error type for `peach-stats`. -use jsonrpc_core::{types::error::Error, ErrorCode}; use probes::ProbeError; +use std::{error, fmt, io::Error as IoError}; +/// Custom error type encapsulating all possible errors when retrieving system +/// statistics. #[derive(Debug)] -pub enum StatError { - CpuStat { source: ProbeError }, - DiskUsage { source: ProbeError }, - LoadAvg { source: ProbeError }, - MemStat { source: ProbeError }, - Uptime { source: io::Error }, +pub enum StatsError { + /// Failed to retrieve CPU statistics. + CpuStat(ProbeError), + /// Failed to retrieve disk usage statistics. + DiskUsage(ProbeError), + /// Failed to retrieve load average statistics. + LoadAvg(ProbeError), + /// Failed to retrieve memory usage statistics. + MemStat(ProbeError), + /// Failed to retrieve system uptime. + Uptime(IoError), } -impl error::Error for StatError {} +impl error::Error for StatsError {} -impl fmt::Display for StatError { +impl fmt::Display for StatsError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - StatError::CpuStat { ref source } => { + StatsError::CpuStat(ref source) => { write!(f, "Failed to retrieve CPU statistics: {}", source) } - StatError::DiskUsage { ref source } => { + StatsError::DiskUsage(ref source) => { write!(f, "Failed to retrieve disk usage statistics: {}", source) } - StatError::LoadAvg { ref source } => { + StatsError::LoadAvg(ref source) => { write!(f, "Failed to retrieve load average statistics: {}", source) } - StatError::MemStat { ref source } => { + StatsError::MemStat(ref source) => { write!(f, "Failed to retrieve memory statistics: {}", source) } - StatError::Uptime { ref source } => { + StatsError::Uptime(ref source) => { write!(f, "Failed to retrieve system uptime: {}", source) } } } } - -impl From for Error { - fn from(err: StatError) -> Self { - match &err { - StatError::CpuStat { source } => Error { - code: ErrorCode::ServerError(-32001), - message: format!("Failed to retrieve CPU statistics: {}", source), - data: None, - }, - StatError::DiskUsage { source } => Error { - code: ErrorCode::ServerError(-32001), - message: format!("Failed to retrieve disk usage statistics: {}", source), - data: None, - }, - StatError::LoadAvg { source } => Error { - code: ErrorCode::ServerError(-32001), - message: format!("Failed to retrieve load average statistics: {}", source), - data: None, - }, - StatError::MemStat { source } => Error { - code: ErrorCode::ServerError(-32001), - message: format!("Failed to retrieve memory statistics: {}", source), - data: None, - }, - StatError::Uptime { source } => Error { - code: ErrorCode::ServerError(-32001), - message: format!("Failed to retrieve system uptime: {}", source), - data: None, - }, - } - } -} diff --git a/peach-stats/src/lib.rs b/peach-stats/src/lib.rs index 00ce6a1..cae7d50 100644 --- a/peach-stats/src/lib.rs +++ b/peach-stats/src/lib.rs @@ -1,103 +1,48 @@ -mod error; -mod stats; -mod structs; +#![warn(missing_docs)] -use std::{env, result::Result}; +//! # peach-stats +//! +//! System statistics retrieval library; designed for use with the PeachCloud platform. +//! +//! Currently offers the following statistics and associated data structures: +//! +//! - CPU: `user`, `system`, `nice`, `idle` (as values or percentages) +//! - Disk usage: `filesystem`, `one_k_blocks`, `one_k_blocks_used`, +//! `one_k_blocks_free`, `used_percentage`, `mountpoint` +//! - Load average: `one`, `five`, `fifteen` +//! - Memory: `total`, `free`, `used` +//! - Uptime: `seconds` +//! +//! ## Example Usage +//! +//! ```rust +//! use peach_stats::{stats, StatsError}; +//! +//! fn main() -> Result<(), StatsError> { +//! let cpu = stats::cpu_stats()?; +//! let cpu_percentages = stats::cpu_stats_percent()?; +//! let disks = stats::disk_usage()?; +//! let load = stats::load_average()?; +//! let mem = stats::mem_stats()?; +//! let uptime = stats::uptime()?; +//! +//! Ok(()) +//! } +//! ``` +//! +//! ## Feature Flags +//! +//! Feature flags are used to offer `Serialize` and `Deserialize` implementations +//! for all `struct` data types provided by this library. These traits are not +//! provided by default. A choice of `miniserde` and `serde` is provided. +//! +//! Define the desired feature in the `Cargo.toml` manifest of your project: +//! +//! ```toml +//! peach-stats = { version = "0.1.0", features = ["miniserde_support"] } +//! ``` -use jsonrpc_core::{IoHandler, Value}; -use jsonrpc_http_server::{AccessControlAllowOrigin, DomainsValidation, ServerBuilder}; -use log::info; +pub mod error; +pub mod stats; -use crate::error::StatError; - -pub fn run() -> Result<(), StatError> { - info!("Starting up."); - - info!("Creating JSON-RPC I/O handler."); - let mut io = IoHandler::default(); - - io.add_method("cpu_stats", move |_| async { - info!("Fetching CPU statistics."); - let stats = stats::cpu_stats()?; - - Ok(Value::String(stats)) - }); - - io.add_method("cpu_stats_percent", move |_| async { - info!("Fetching CPU statistics as percentages."); - let stats = stats::cpu_stats_percent()?; - - Ok(Value::String(stats)) - }); - - io.add_method("disk_usage", move |_| async { - info!("Fetching disk usage statistics."); - let disks = stats::disk_usage()?; - - Ok(Value::String(disks)) - }); - - io.add_method("load_average", move |_| async { - info!("Fetching system load average statistics."); - let avg = stats::load_average()?; - - Ok(Value::String(avg)) - }); - - io.add_method("mem_stats", move |_| async { - info!("Fetching current memory statistics."); - let mem = stats::mem_stats()?; - - Ok(Value::String(mem)) - }); - - io.add_method("ping", |_| async { - Ok(Value::String("success".to_string())) - }); - - io.add_method("uptime", move |_| async { - info!("Fetching system uptime."); - let uptime = stats::uptime()?; - - Ok(Value::String(uptime)) - }); - - let http_server = env::var("PEACH_OLED_STATS").unwrap_or_else(|_| "127.0.0.1:5113".to_string()); - - info!("Starting JSON-RPC server on {}.", http_server); - let server = ServerBuilder::new(io) - .cors(DomainsValidation::AllowOnly(vec![ - AccessControlAllowOrigin::Null, - ])) - .start_http( - &http_server - .parse() - .expect("Invalid HTTP address and port combination"), - ) - .expect("Unable to start RPC server"); - - info!("Listening for requests."); - server.wait(); - - Ok(()) -} - -#[cfg(test)] -mod tests { - use super::*; - use jsonrpc_test as test_rpc; - - // test to ensure correct success response - #[test] - fn rpc_success() { - let rpc = { - let mut io = IoHandler::new(); - io.add_method("rpc_success_response", |_| async { - Ok(Value::String("success".into())) - }); - test_rpc::Rpc::from(io) - }; - - assert_eq!(rpc.request("rpc_success_response", &()), r#""success""#); - } -} +pub use crate::error::StatsError; diff --git a/peach-stats/src/main.rs b/peach-stats/src/main.rs deleted file mode 100644 index 55a11ec..0000000 --- a/peach-stats/src/main.rs +++ /dev/null @@ -1,14 +0,0 @@ -use std::process; - -use log::error; - -fn main() { - // initialize the logger - env_logger::init(); - - // handle errors returned from `run` - if let Err(e) = peach_stats::run() { - error!("Application error: {}", e); - process::exit(1); - } -} diff --git a/peach-stats/src/stats.rs b/peach-stats/src/stats.rs index 15c9031..0a0914d 100644 --- a/peach-stats/src/stats.rs +++ b/peach-stats/src/stats.rs @@ -1,14 +1,96 @@ +//! System statistics retrieval functions and associated data types. + use std::result::Result; -use miniserde::json; +#[cfg(feature = "miniserde_support")] +use miniserde::{Deserialize, Serialize}; + +#[cfg(feature = "serde_support")] +use serde::{Deserialize, Serialize}; + use probes::{cpu, disk_usage, load, memory}; use systemstat::{Platform, System}; -use crate::error::StatError; -use crate::structs::{CpuStat, CpuStatPercentages, DiskUsage, LoadAverage, MemStat}; +use crate::error::StatsError; -pub fn cpu_stats() -> Result { - let cpu_stats = cpu::proc::read().map_err(|source| StatError::CpuStat { source })?; +/// CPU statistics. +#[derive(Debug)] +#[cfg_attr(feature = "miniserde_support", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))] +pub struct CpuStat { + /// Time spent running user space (application) code. + pub user: u64, + /// Time spent running kernel code. + pub system: u64, + /// Time spent doing nothing. + pub idle: u64, + /// Time spent running user space processes which have been niced. + pub nice: u64, +} + +/// CPU statistics as percentages. +#[derive(Debug)] +#[cfg_attr(feature = "miniserde_support", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))] +pub struct CpuStatPercentages { + /// Time spent running user space (application) code. + pub user: f32, + /// Time spent running kernel code. + pub system: f32, + /// Time spent doing nothing. + pub idle: f32, + /// Time spent running user space processes which have been niced. + pub nice: f32, +} + +/// Disk usage statistics. +#[derive(Debug)] +#[cfg_attr(feature = "miniserde_support", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))] +pub struct DiskUsage { + /// Filesystem device path. + pub filesystem: Option, + /// Total amount of disk space as a number of 1,000 kilobyte blocks. + pub one_k_blocks: u64, + /// Total amount of used disk space as a number of 1,000 kilobyte blocks. + pub one_k_blocks_used: u64, + /// Total amount of free / available disk space as a number of 1,000 kilobyte blocks. + pub one_k_blocks_free: u64, + /// Total amount of used disk space as a percentage. + pub used_percentage: u32, + /// Mountpoint of the disk / partition. + pub mountpoint: String, +} + +/// Load average statistics. +#[derive(Debug)] +#[cfg_attr(feature = "miniserde_support", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))] +pub struct LoadAverage { + /// Average computational work performed over the past minute. + pub one: f32, + /// Average computational work performed over the past five minutes. + pub five: f32, + /// Average computational work performed over the past fifteen minutes. + pub fifteen: f32, +} + +/// Memory statistics. +#[derive(Debug)] +#[cfg_attr(feature = "miniserde_support", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))] +pub struct MemStat { + /// Total amount of physical memory in kilobytes. + pub total: u64, + /// Total amount of free / available physical memory in kilobytes. + pub free: u64, + /// Total amount of used physical memory in kilobytes. + pub used: u64, +} + +/// Retrieve the current CPU statistics. +pub fn cpu_stats() -> Result { + let cpu_stats = cpu::proc::read().map_err(StatsError::CpuStat)?; let s = cpu_stats.stat; let cpu = CpuStat { user: s.user, @@ -16,13 +98,13 @@ pub fn cpu_stats() -> Result { nice: s.nice, idle: s.idle, }; - let json_cpu = json::to_string(&cpu); - Ok(json_cpu) + Ok(cpu) } -pub fn cpu_stats_percent() -> Result { - let cpu_stats = cpu::proc::read().map_err(|source| StatError::CpuStat { source })?; +/// Retrieve the current CPU statistics as percentages. +pub fn cpu_stats_percent() -> Result { + let cpu_stats = cpu::proc::read().map_err(StatsError::CpuStat)?; let s = cpu_stats.stat.in_percentages(); let cpu = CpuStatPercentages { user: s.user, @@ -30,13 +112,13 @@ pub fn cpu_stats_percent() -> Result { nice: s.nice, idle: s.idle, }; - let json_cpu = json::to_string(&cpu); - Ok(json_cpu) + Ok(cpu) } -pub fn disk_usage() -> Result { - let disks = disk_usage::read().map_err(|source| StatError::DiskUsage { source })?; +/// Retrieve the current disk usage statistics for each available disk / partition. +pub fn disk_usage() -> Result, StatsError> { + let disks = disk_usage::read().map_err(StatsError::DiskUsage)?; let mut disk_usages = Vec::new(); for d in disks { let disk = DiskUsage { @@ -49,42 +131,39 @@ pub fn disk_usage() -> Result { }; disk_usages.push(disk); } - let json_disks = json::to_string(&disk_usages); - Ok(json_disks) + Ok(disk_usages) } -pub fn load_average() -> Result { - let l = load::read().map_err(|source| StatError::LoadAvg { source })?; +/// Retrieve the current load average statistics. +pub fn load_average() -> Result { + let l = load::read().map_err(StatsError::LoadAvg)?; let load_avg = LoadAverage { one: l.one, five: l.five, fifteen: l.fifteen, }; - let json_load_avg = json::to_string(&load_avg); - Ok(json_load_avg) + Ok(load_avg) } -pub fn mem_stats() -> Result { - let m = memory::read().map_err(|source| StatError::MemStat { source })?; +/// Retrieve the current memory usage statistics. +pub fn mem_stats() -> Result { + let m = memory::read().map_err(StatsError::MemStat)?; let mem = MemStat { total: m.total(), free: m.free(), used: m.used(), }; - let json_mem = json::to_string(&mem); - Ok(json_mem) + Ok(mem) } -pub fn uptime() -> Result { +/// Retrieve the system uptime in seconds. +pub fn uptime() -> Result { let sys = System::new(); - let uptime = sys - .uptime() - .map_err(|source| StatError::Uptime { source })?; + let uptime = sys.uptime().map_err(StatsError::Uptime)?; let uptime_secs = uptime.as_secs(); - let json_uptime = json::to_string(&uptime_secs); - Ok(json_uptime) + Ok(uptime_secs) } diff --git a/peach-stats/src/structs.rs b/peach-stats/src/structs.rs deleted file mode 100644 index f307879..0000000 --- a/peach-stats/src/structs.rs +++ /dev/null @@ -1,41 +0,0 @@ -use miniserde::Serialize; - -#[derive(Debug, Serialize)] -pub struct CpuStat { - pub user: u64, - pub system: u64, - pub idle: u64, - pub nice: u64, -} - -#[derive(Debug, Serialize)] -pub struct CpuStatPercentages { - pub user: f32, - pub system: f32, - pub idle: f32, - pub nice: f32, -} - -#[derive(Debug, Serialize)] -pub struct DiskUsage { - pub filesystem: Option, - pub one_k_blocks: u64, - pub one_k_blocks_used: u64, - pub one_k_blocks_free: u64, - pub used_percentage: u32, - pub mountpoint: String, -} - -#[derive(Debug, Serialize)] -pub struct LoadAverage { - pub one: f32, - pub five: f32, - pub fifteen: f32, -} - -#[derive(Debug, Serialize)] -pub struct MemStat { - pub total: u64, - pub free: u64, - pub used: u64, -} diff --git a/peach-web/Cargo.toml b/peach-web/Cargo.toml index c94e96c..5bc93b3 100644 --- a/peach-web/Cargo.toml +++ b/peach-web/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "peach-web" -version = "0.4.11" +version = "0.4.12" authors = ["Andrew Reid "] 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." @@ -38,17 +38,16 @@ maintenance = { status = "actively-developed" } env_logger = "0.8" log = "0.4" nest = "1.0.0" +openssl = { version = "0.10", features = ["vendored"] } peach-lib = { path = "../peach-lib" } percent-encoding = "2.1.0" +regex = "1" rocket = { version = "0.5.0-rc.1", features = ["json", "secrets"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" snafu = "0.6" tera = { version = "1.12.1", features = ["builtins"] } -websocket = "0.26" -regex = "1" xdg = "2.2.0" -openssl = { version = "0.10", features = ["vendored"] } [dependencies.rocket_dyn_templates] version = "0.1.0-rc.1" diff --git a/peach-web/README.md b/peach-web/README.md index f8185cd..018ab8d 100644 --- a/peach-web/README.md +++ b/peach-web/README.md @@ -1,6 +1,6 @@ # peach-web -[![Build Status](https://travis-ci.com/peachcloud/peach-web.svg?branch=master)](https://travis-ci.com/peachcloud/peach-web) ![Generic badge](https://img.shields.io/badge/version-0.4.6-.svg) +[![Build Status](https://travis-ci.com/peachcloud/peach-web.svg?branch=master)](https://travis-ci.com/peachcloud/peach-web) ![Generic badge](https://img.shields.io/badge/version-0.4.12-.svg) ## Web Interface for PeachCloud @@ -39,12 +39,22 @@ _Note: Networking functionality requires peach-network microservice to be runnin ### Environment +**Deployment Mode** + The web application deployment mode is configured with the `ROCKET_ENV` environment variable: `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. +**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`: `export RUST_LOG=info` diff --git a/peach-web/Rocket.toml b/peach-web/Rocket.toml index d8fe73c..d46d8fb 100644 --- a/peach-web/Rocket.toml +++ b/peach-web/Rocket.toml @@ -1,5 +1,7 @@ [development] template_dir = "templates/" +disable_auth = true [production] template_dir = "templates/" +disable_auth = false diff --git a/peach-web/src/routes/authentication.rs b/peach-web/src/routes/authentication.rs index 035bde5..2743e19 100644 --- a/peach-web/src/routes/authentication.rs +++ b/peach-web/src/routes/authentication.rs @@ -1,10 +1,13 @@ use log::info; use rocket::form::{Form, FromForm}; -use rocket::request::FlashMessage; +use rocket::http::{Cookie, CookieJar, Status}; +use rocket::request::{self, FlashMessage, FromRequest, Request}; use rocket::response::{Flash, Redirect}; -use rocket::serde::json::Json; -use rocket::serde::{Deserialize, Serialize}; -use rocket::{get, post}; +use rocket::serde::{ + json::{Json, Value}, + Deserialize, Serialize, +}; +use rocket::{get, post, Config}; use rocket_dyn_templates::Template; use peach_lib::error::PeachError; @@ -12,9 +15,6 @@ use peach_lib::password_utils; use crate::error::PeachWebError; 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 @@ -42,14 +42,27 @@ impl<'r> FromRequest<'r> for Authenticated { type Error = LoginError; async fn from_request(req: &'r Request<'_>) -> request::Outcome { - 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)), + // check for `disable_auth` config value; set to `false` if unset + // can be set via the `ROCKET_DISABLE_AUTH` environment variable + // - env var, if set, takes precedence over value defined in `Rocket.toml` + let authentication_is_disabled: bool = match Config::figment().find_value("disable_auth") { + // deserialize the boolean value; set to `false` if an error is encountered + Ok(value) => value.deserialize().unwrap_or(false), + Err(_) => false, + }; + 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)), + } } } } diff --git a/peach-web/src/routes/settings/dns.rs b/peach-web/src/routes/settings/dns.rs index 8ef3e21..4c3f952 100644 --- a/peach-web/src/routes/settings/dns.rs +++ b/peach-web/src/routes/settings/dns.rs @@ -52,7 +52,7 @@ pub fn save_dns_configuration(dns_form: DnsForm) -> Result<(), PeachWebError> { info!("Failed to register dyndns domain: {:?}", err); // json response for failed update let msg: String = match err { - PeachError::JsonRpcClientCore { source } => { + PeachError::JsonRpcClientCore(source) => { match source { Error(ErrorKind::JsonRpcError(err), _state) => match err.code { ErrorCode::ServerError(-32030) => { diff --git a/peach-web/src/tests.rs b/peach-web/src/tests.rs index 712fa38..e95c794 100644 --- a/peach-web/src/tests.rs +++ b/peach-web/src/tests.rs @@ -4,17 +4,29 @@ use std::io::Read; use rocket::http::{ContentType, Status}; use rocket::local::blocking::Client; use rocket::serde::json::{json, Value}; +use rocket::{Build, Config, Rocket}; use crate::utils::build_json_response; 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 { + // 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 fn test_query_file(path: &str, file: T, status: Status) where T: Into>, { - let client = Client::tracked(init_rocket()).unwrap(); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).unwrap(); let response = client.get(path).dispatch(); assert_eq!(response.status(), status); @@ -39,7 +51,7 @@ fn read_file_content(path: &str) -> Vec { #[test] fn index_html() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); @@ -54,7 +66,7 @@ fn index_html() { #[test] fn help_html() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/help").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); @@ -64,7 +76,7 @@ fn help_html() { #[test] fn login_html() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/login").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); @@ -74,7 +86,7 @@ fn login_html() { #[test] fn logout_html() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/logout").dispatch(); // check for 303 status (redirect to "/login") assert_eq!(response.status(), Status::SeeOther); @@ -83,7 +95,7 @@ fn logout_html() { #[test] fn power_html() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/power").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); @@ -97,7 +109,7 @@ NOTE: these tests are comment-out for the moment, due to the fact that they invo #[test] fn reboot() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/power/reboot").dispatch(); // check for redirect assert_eq!(response.status(), Status::SeeOther); @@ -105,7 +117,7 @@ fn reboot() { #[test] fn shutdown() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/power/shutdown").dispatch(); // check for redirect assert_eq!(response.status(), Status::SeeOther); @@ -116,7 +128,7 @@ fn shutdown() { #[test] fn block() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client .post("/scuttlebutt/block") .header(ContentType::Form) @@ -127,7 +139,7 @@ fn block() { #[test] fn blocks_html() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/scuttlebutt/blocks").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); @@ -137,7 +149,7 @@ fn blocks_html() { #[test] fn follow() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client .post("/scuttlebutt/follow") .header(ContentType::Form) @@ -149,7 +161,7 @@ fn follow() { #[test] fn follows_html() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/scuttlebutt/follows").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); @@ -159,7 +171,7 @@ fn follows_html() { #[test] fn followers_html() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/scuttlebutt/followers").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); @@ -169,7 +181,7 @@ fn followers_html() { #[test] fn friends_html() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/scuttlebutt/friends").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); @@ -179,7 +191,7 @@ fn friends_html() { #[test] fn peers_html() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/scuttlebutt/peers").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); @@ -189,7 +201,7 @@ fn peers_html() { #[test] fn private_html() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/scuttlebutt/private").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); @@ -199,7 +211,7 @@ fn private_html() { #[test] fn profile_html() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/scuttlebutt/profile").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); @@ -209,7 +221,7 @@ fn profile_html() { #[test] fn publish_post() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client .post("/scuttlebutt/publish") .header(ContentType::Form) @@ -220,7 +232,7 @@ fn publish_post() { #[test] fn unfollow() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client .post("/scuttlebutt/unfollow") .header(ContentType::Form) @@ -233,7 +245,7 @@ fn unfollow() { #[test] fn admin_settings_menu_html() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/settings/admin").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); @@ -245,7 +257,7 @@ fn admin_settings_menu_html() { #[test] fn add_admin_html() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/settings/admin/add").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); @@ -258,7 +270,7 @@ fn add_admin_html() { #[test] fn add_admin() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client .post("/settings/admin/add") .header(ContentType::Form) @@ -270,21 +282,21 @@ fn add_admin() { #[test] fn change_password_html() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/settings/admin/change_password").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); let body = response.into_string().unwrap(); assert!(body.contains("Change Password")); - assert!(body.contains("Old Password")); - assert!(body.contains("Enter New Password")); - assert!(body.contains("Re-Enter New Password")); + assert!(body.contains("Current password")); + assert!(body.contains("New password")); + assert!(body.contains("New password duplicate")); assert!(body.contains("Save")); } #[test] fn configure_admin_html() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/settings/admin/configure").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); @@ -296,7 +308,7 @@ fn configure_admin_html() { #[test] fn forgot_password_html() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/settings/admin/forgot_password").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); @@ -308,7 +320,7 @@ fn forgot_password_html() { #[test] fn network_settings_menu_html() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/settings/network").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); @@ -318,7 +330,7 @@ fn network_settings_menu_html() { #[test] fn deploy_ap() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/settings/network/ap/activate").dispatch(); // check for 303 status (redirect) assert_eq!(response.status(), Status::SeeOther); @@ -327,7 +339,7 @@ fn deploy_ap() { #[test] fn dns_settings_html() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/settings/network/dns").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); @@ -341,7 +353,7 @@ fn dns_settings_html() { #[test] fn list_aps_html() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/settings/network/wifi").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); @@ -353,7 +365,7 @@ fn list_aps_html() { // TODO: needs further testing once template has been refactored #[test] fn ap_details_html() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/settings/network/wifi?ssid=Home").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); @@ -363,7 +375,7 @@ fn ap_details_html() { #[test] fn deploy_client() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/settings/network/wifi/activate").dispatch(); // check for 303 status (redirect) assert_eq!(response.status(), Status::SeeOther); @@ -372,7 +384,7 @@ fn deploy_client() { #[test] fn add_ap_html() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/settings/network/wifi/add").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); @@ -386,7 +398,7 @@ fn add_ap_html() { #[test] fn add_ap_ssid_html() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client .get("/settings/network/wifi/add?ssid=Home") .dispatch(); @@ -402,7 +414,7 @@ fn add_ap_ssid_html() { #[test] fn add_credentials() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client .post("/settings/network/wifi/add") .header(ContentType::Form) @@ -414,7 +426,7 @@ fn add_credentials() { #[test] fn forget_wifi() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client .post("/settings/network/wifi/forget") .header(ContentType::Form) @@ -426,7 +438,7 @@ fn forget_wifi() { #[test] fn modify_password() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client .post("/settings/network/wifi/modify") .header(ContentType::Form) @@ -438,7 +450,7 @@ fn modify_password() { #[test] fn data_usage_html() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/settings/network/wifi/usage").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); @@ -453,7 +465,7 @@ fn data_usage_html() { #[test] fn scuttlebutt_settings_menu_html() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/settings/scuttlebutt").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); @@ -472,7 +484,7 @@ fn scuttlebutt_settings_menu_html() { #[test] fn status_html() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/status").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); @@ -485,7 +497,7 @@ fn status_html() { #[test] fn network_status_html() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client.get("/status/network").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::HTML)); @@ -502,7 +514,7 @@ fn network_status_html() { #[test] fn activate_ap() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client .post("/api/v1/network/activate_ap") .header(ContentType::JSON) @@ -513,7 +525,7 @@ fn activate_ap() { #[test] fn activate_client() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client .post("/api/v1/network/activate_client") .header(ContentType::JSON) @@ -524,7 +536,7 @@ fn activate_client() { #[test] fn return_ip() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client .get("/api/v1/network/ip") .header(ContentType::JSON) @@ -538,7 +550,7 @@ fn return_ip() { #[test] fn return_rssi() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client .get("/api/v1/network/rssi") .header(ContentType::JSON) @@ -551,7 +563,7 @@ fn return_rssi() { #[test] fn return_ssid() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client .get("/api/v1/network/ssid") .header(ContentType::JSON) @@ -564,7 +576,7 @@ fn return_ssid() { #[test] fn return_state() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client .get("/api/v1/network/state") .header(ContentType::JSON) @@ -579,7 +591,7 @@ fn return_state() { #[test] fn return_status() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client .get("/api/v1/network/status") .header(ContentType::JSON) @@ -592,7 +604,7 @@ fn return_status() { #[test] fn scan_networks() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client .get("/api/v1/network/wifi") .header(ContentType::JSON) @@ -605,7 +617,7 @@ fn scan_networks() { #[test] fn add_wifi() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client .post("/api/v1/network/wifi") .header(ContentType::JSON) @@ -619,7 +631,7 @@ fn add_wifi() { #[test] fn remove_wifi() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client .post("/api/v1/network/wifi/forget") .header(ContentType::JSON) @@ -633,7 +645,7 @@ fn remove_wifi() { #[test] fn new_password() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client .post("/api/v1/network/wifi/modify") .header(ContentType::JSON) @@ -647,7 +659,7 @@ fn new_password() { #[test] fn ping_pong() { - let client = Client::tracked(init_rocket()).expect("valid rocket instance"); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).expect("valid rocket instance"); let response = client .get("/api/v1/ping") .header(ContentType::JSON) @@ -709,7 +721,7 @@ fn invalid_path() { #[test] fn invalid_get_request() { - let client = Client::tracked(init_rocket()).unwrap(); + let client = Client::tracked(init_test_rocket(DISABLE_AUTH)).unwrap(); // try to get a path that doesn't exist let res = client diff --git a/peach-web/static/css/peachcloud.css b/peach-web/static/css/peachcloud.css index 8e65b6f..8c767f0 100644 --- a/peach-web/static/css/peachcloud.css +++ b/peach-web/static/css/peachcloud.css @@ -220,12 +220,18 @@ body { } .capsule-container { - margin-left: 2rem; - margin-right: 2rem; - padding-top: 1rem; + margin-left: 1rem; + margin-right: 1rem; padding-bottom: 1rem; } +@media only screen and (min-width: 600px) { + .capsule-container { + margin-left: 0; + margin-right: 0; + } +} + /* * CARDS */ @@ -235,6 +241,7 @@ body { max-height: 90vh; position: relative; width: 100%; + margin-top: 1rem; } @media only screen and (min-width: 600px) { @@ -248,8 +255,6 @@ body { .card-container { justify-content: center; padding: 0.5rem; - /* padding-top: 1rem; */ - /* padding-bottom: 1rem; */ } .form-container { @@ -560,6 +565,7 @@ html { font-size: var(--font-size-6); margin-left: 2rem; margin-right: 2rem; + margin-top: 1rem; } /* diff --git a/peach-web/static/js/change_password.js b/peach-web/static/js/change_password.js index 85bc2b1..f7efc93 100644 --- a/peach-web/static/js/change_password.js +++ b/peach-web/static/js/change_password.js @@ -1,16 +1,28 @@ /* -* 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 -PEACH.add = function() { +PEACH_AUTH.changePassword = function() { document.addEventListener('DOMContentLoaded', function() { document.body.addEventListener('submit', function(e) { // prevent redirect on button press (default behavior) e.preventDefault(); // capture form data var formElement = document.querySelector("form"); - // create form data object from the wifiCreds form element + // create form data object from the changePassword form element var formData = new FormData(formElement); var object = {}; // assign values from form @@ -22,7 +34,7 @@ PEACH.add = function() { var jsonData = JSON.stringify(object); // write in-progress status message to ui PEACH.flashMsg("info", "Saving new password."); - // send add_wifi POST request + // send change_password POST request fetch("/api/v1/admin/change_password", { method: "post", headers: { @@ -41,5 +53,5 @@ PEACH.add = function() { }); } -var addInstance = PEACH; -addInstance.add(); +var changePassInstance = PEACH_AUTH; +changePassInstance.changePassword(); diff --git a/peach-web/static/js/common.js b/peach-web/static/js/common.js index 05167d2..1197e57 100644 --- a/peach-web/static/js/common.js +++ b/peach-web/static/js/common.js @@ -43,5 +43,4 @@ PEACH.flashMsg = function(status, msg) { } } -var addInstance = PEACH; -addInstance.add(); +var commonInstance = PEACH; diff --git a/peach-web/static/js/configure_dns.js b/peach-web/static/js/configure_dns.js index 8abef30..ff677ec 100644 --- a/peach-web/static/js/configure_dns.js +++ b/peach-web/static/js/configure_dns.js @@ -1,9 +1,9 @@ /* behavioural layer for the `configure_dns.html.tera` template, -corresponding to the web route `/network/dns` +corresponding to the web route `/settings/network/dns` - - intercept button click for add (form submission of credentials) + - intercept button click for save (form submission of dns settings) - perform json api call - update the dom @@ -12,14 +12,14 @@ corresponding to the web route `/network/dns` var PEACH_DNS = {}; // catch click of 'Add' button and make POST request -PEACH_DNS.add = function() { +PEACH_DNS.configureDns = function() { document.addEventListener('DOMContentLoaded', function() { document.body.addEventListener('submit', function(e) { // prevent redirect on button press (default behavior) e.preventDefault(); // capture form data var formElement = document.querySelector("form"); - // create form data object from the wifiCreds form element + // create form data object from the configureDNS form element var formData = new FormData(formElement); var object = {}; // set checkbox to false (the value is only passed to formData if it is "on") @@ -36,7 +36,7 @@ PEACH_DNS.add = function() { console.log(object); var jsonData = JSON.stringify(object); // write in-progress status message to ui - PEACH_DNS.flashMsg("info", "Saving new DNS configurations"); + PEACH.flashMsg("info", "Saving new DNS configurations"); // send add_wifi POST request fetch("/api/v1/network/dns/configure", { method: "post", @@ -50,49 +50,14 @@ PEACH_DNS.add = function() { }) .then( (jsonData) => { // write json response message to ui - PEACH_DNS.flashMsg(jsonData.status, jsonData.msg); + PEACH.flashMsg(jsonData.status, jsonData.msg); let statusIndicator = document.getElementById("dyndns-status-indicator"); - statusIndicator.remove(); - + // only remove the "dyndns-status-indicator" element if it exists + if (statusIndicator != null ) statusIndicator.remove(); }) }, false); }); } -// display a message by appending a paragraph element -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(); +var configureDnsInstance = PEACH_DNS; +configureDnsInstance.configureDns(); diff --git a/peach-web/static/js/network_add.js b/peach-web/static/js/network_add.js index ccf68a1..a441234 100644 --- a/peach-web/static/js/network_add.js +++ b/peach-web/static/js/network_add.js @@ -10,7 +10,6 @@ corresponding to the web route `/network/wifi/add` methods: PEACH_NETWORK.add(); - PEACH_NETWORK.flashMsg(status, msg); */ @@ -34,7 +33,7 @@ PEACH_NETWORK.add = function() { // perform json serialization var jsonData = JSON.stringify(object); // write in-progress status message to ui - PEACH_NETWORK.flashMsg("info", "Adding WiFi credentials..."); + PEACH.flashMsg("info", "Adding WiFi credentials..."); // send add_wifi POST request fetch("/api/v1/network/wifi", { method: "post", @@ -48,46 +47,11 @@ PEACH_NETWORK.add = function() { }) .then( (jsonData) => { // write json response message to ui - PEACH_NETWORK.flashMsg(jsonData.status, jsonData.msg); + PEACH.flashMsg(jsonData.status, jsonData.msg); }) }, 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; addInstance.add(); diff --git a/peach-web/static/js/network_card.js b/peach-web/static/js/network_card.js index 79fa72a..3026a72 100644 --- a/peach-web/static/js/network_card.js +++ b/peach-web/static/js/network_card.js @@ -1,7 +1,7 @@ /* behavioural layer for the `network_card.html.tera` template, -corresponding to the web route `/network` +corresponding to the web route `/settings/network` - intercept form submissions - perform json api calls @@ -11,10 +11,8 @@ methods: PEACH_NETWORK.activateAp(); PEACH_NETWORK.activateClient(); - PEACH_NETWORK.apOnline(); - PEACH_NETWORK.clientOffline(); - PEACH_NETWORK.clientOnline(); - PEACH_NETWORK.flashMsg(status, msg); + PEACH_NETWORK.apMode(); + PEACH_NETWORK.clientMode(); */ @@ -29,7 +27,7 @@ PEACH_NETWORK.activateAp = function() { // prevent form submission (default behavior) e.preventDefault(); // write in-progress status message to ui - PEACH_NETWORK.flashMsg("info", "Deploying access point..."); + PEACH.flashMsg("info", "Deploying access point..."); // send activate_ap POST request fetch("/api/v1/network/activate_ap", { method: "post", @@ -44,10 +42,10 @@ PEACH_NETWORK.activateAp = function() { .then( (jsonData) => { console.log(jsonData.msg); // write json response message to ui - PEACH_NETWORK.flashMsg(jsonData.status, jsonData.msg); + PEACH.flashMsg(jsonData.status, jsonData.msg); // if ap activation is successful, update the ui if (jsonData.status === "success") { - PEACH_NETWORK.apOnline(); + PEACH_NETWORK.apMode(); } }) }, false); @@ -64,7 +62,7 @@ PEACH_NETWORK.activateClient = function() { // prevent form submission (default behavior) e.preventDefault(); // write in-progress status message to ui - PEACH_NETWORK.flashMsg("info", "Enabling WiFi client..."); + PEACH.flashMsg("info", "Enabling WiFi client..."); // send activate_ap POST request fetch("/api/v1/network/activate_client", { method: "post", @@ -79,10 +77,10 @@ PEACH_NETWORK.activateClient = function() { .then( (jsonData) => { console.log(jsonData.msg); // write json response message to ui - PEACH_NETWORK.flashMsg(jsonData.status, jsonData.msg); + PEACH.flashMsg(jsonData.status, jsonData.msg); // if client activation is successful, update the ui if (jsonData.status === "success") { - PEACH_NETWORK.clientOnline(); + PEACH_NETWORK.clientMode(); } }) }, false); @@ -90,21 +88,12 @@ PEACH_NETWORK.activateClient = function() { }); } -// update ui for access point mode (status: online) -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"; - +// replace 'Deploy Access Point' button with 'Enable WiFi' button +PEACH_NETWORK.apMode = function() { // create Enable WiFi button and add it to button div var wifiButton = document.createElement("A"); wifiButton.className = "button center"; - wifiButton.href = "/network/wifi/activate"; + wifiButton.href = "/settings/network/wifi/activate"; wifiButton.id = "connectWifi"; var label = "Enable WiFi"; var buttonText = document.createTextNode(label); @@ -114,88 +103,31 @@ PEACH_NETWORK.apOnline = function() { let buttons = document.getElementById("buttons"); buttons.appendChild(wifiButton); - // remove the old 'Activate Access Point' button + // remove the old 'Deploy Access Point' button let apButton = document.getElementById("deployAccessPoint"); - apButton.style = "display: none;"; + apButton.remove(); } -// update ui for wifi client mode (status: online) -PEACH_NETWORK.clientOnline = function() { - console.log('Activating Client Mode'); +// replace 'Enable WiFi' button with 'Deploy Access Point' button +PEACH_NETWORK.clientMode = function() { + // create Deploy Access Point button and add it to button div + 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); - // update network mode and status (icon & label) - let i = document.getElementById("netModeIcon"); - i.className = "center icon icon-active"; - i.src = "icons/wifi.svg"; - let l = document.getElementById("netModeLabel"); - l.textContent = "ONLINE"; + // append the new button to the buttons div + let buttons = document.getElementById("buttons"); + buttons.appendChild(apButton); - // TODO: think about updates for buttons (transition from ap mode) -} - -// 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); - } + // remove the old 'Enable Wifi' button + let wifiButton = document.getElementById("connectWifi"); + wifiButton.remove(); } var networkInstance = PEACH_NETWORK; networkInstance.activateAp(); 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() - -*/ diff --git a/peach-web/static/js/network_detail.js b/peach-web/static/js/network_detail.js index 6d294ae..7a5baae 100644 --- a/peach-web/static/js/network_detail.js +++ b/peach-web/static/js/network_detail.js @@ -1,7 +1,7 @@ /* behavioural layer for the `network_detail.html.tera` template, -corresponding to the web route `/network/wifi?` +corresponding to the web route `/settings/network/wifi?` - intercept button clicks for connect, disconnect and forget - perform json api call @@ -12,7 +12,6 @@ methods: PEACH_NETWORK.connect(); PEACH_NETWORK.disconnect(); PEACH_NETWORK.forget(); - PEACH_NETWORK.flashMsg(status, msg); */ @@ -33,7 +32,7 @@ PEACH_NETWORK.connect = function() { // perform json serialization var jsonData = JSON.stringify(ssidData); // write in-progress status message to ui - PEACH_NETWORK.flashMsg("info", "Connecting to access point..."); + PEACH.flashMsg("info", "Connecting to access point..."); // send add_wifi POST request fetch("/api/v1/network/wifi/connect", { method: "post", @@ -47,7 +46,7 @@ PEACH_NETWORK.connect = function() { }) .then( (jsonData) => { // write json response message to ui - PEACH_NETWORK.flashMsg(jsonData.status, jsonData.msg); + PEACH.flashMsg(jsonData.status, jsonData.msg); }) }, false); }; @@ -69,7 +68,7 @@ PEACH_NETWORK.disconnect = function() { // perform json serialization var jsonData = JSON.stringify(ssidData); // write in-progress status message to ui - PEACH_NETWORK.flashMsg("info", "Disconnecting from access point..."); + PEACH.flashMsg("info", "Disconnecting from access point..."); // send disconnect_wifi POST request fetch("/api/v1/network/wifi/disconnect", { method: "post", @@ -83,7 +82,7 @@ PEACH_NETWORK.disconnect = function() { }) .then( (jsonData) => { // write json response message to ui - PEACH_NETWORK.flashMsg(jsonData.status, jsonData.msg); + PEACH.flashMsg(jsonData.status, jsonData.msg); }) }, false); }; @@ -105,7 +104,7 @@ PEACH_NETWORK.forget = function() { // perform json serialization var jsonData = JSON.stringify(ssidData); // write in-progress status message to ui - PEACH_NETWORK.flashMsg("info", "Removing credentials for access point..."); + PEACH.flashMsg("info", "Removing credentials for access point..."); // send forget_ap POST request fetch("/api/v1/network/wifi/forget", { method: "post", @@ -119,48 +118,13 @@ PEACH_NETWORK.forget = function() { }) .then( (jsonData) => { // write json response message to ui - PEACH_NETWORK.flashMsg(jsonData.status, jsonData.msg); + PEACH.flashMsg(jsonData.status, jsonData.msg); }) }, 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; detailInstance.connect(); detailInstance.disconnect(); diff --git a/peach-web/static/js/network_modify.js b/peach-web/static/js/network_modify.js index 4dce16b..0ed9a50 100644 --- a/peach-web/static/js/network_modify.js +++ b/peach-web/static/js/network_modify.js @@ -9,7 +9,6 @@ behavioural layer for the `network_modify.html.tera` template methods: PEACH_NETWORK.modify(); - PEACH_NETWORK.flashMsg(status, msg); */ @@ -33,7 +32,7 @@ PEACH_NETWORK.modify = function() { // perform json serialization var jsonData = JSON.stringify(object); // write in-progress status message to ui - PEACH_NETWORK.flashMsg("info", "Updating WiFi password..."); + PEACH.flashMsg("info", "Updating WiFi password..."); // send new_password POST request fetch("/api/v1/network/wifi/modify", { method: "post", @@ -47,46 +46,11 @@ PEACH_NETWORK.modify = function() { }) .then( (jsonData) => { // write json response message to ui - PEACH_NETWORK.flashMsg(jsonData.status, jsonData.msg); + PEACH.flashMsg(jsonData.status, jsonData.msg); }) }, 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; modifyInstance.modify(); diff --git a/peach-web/static/js/network_usage.js b/peach-web/static/js/network_usage.js index 236a2d3..a12efea 100644 --- a/peach-web/static/js/network_usage.js +++ b/peach-web/static/js/network_usage.js @@ -1,7 +1,7 @@ /* behavioural layer for the `network_usage.html.tera` template, -corresponding to the web route `/network/wifi/usage` +corresponding to the web route `/settings/network/wifi/usage` - intercept form submissions - perform json api calls @@ -13,7 +13,6 @@ methods: PEACH_NETWORK.resetUsage(); PEACH_NETWORK.toggleWarning(); PEACH_NETWORK.toggleCutoff(); - PEACH_NETWORK.flashMsg(status, msg); */ @@ -51,7 +50,7 @@ PEACH_NETWORK.updateAlerts = function() { }) .then( (jsonData) => { // write json response message to ui - PEACH_NETWORK.flashMsg(jsonData.status, jsonData.msg); + PEACH.flashMsg(jsonData.status, jsonData.msg); }) }, false); }); @@ -79,7 +78,7 @@ PEACH_NETWORK.resetUsage = function() { .then( (jsonData) => { console.log(jsonData.msg); // write json response message to ui - PEACH_NETWORK.flashMsg(jsonData.status, jsonData.msg); + PEACH.flashMsg(jsonData.status, jsonData.msg); // if reset is successful, update the ui if (jsonData.status === "success") { console.log(jsonData.data); @@ -133,39 +132,6 @@ 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; usageInstance.resetUsage(); usageInstance.toggleWarning(); diff --git a/peach-web/static/js/power_menu.js b/peach-web/static/js/power_menu.js index 91e22e0..6841414 100644 --- a/peach-web/static/js/power_menu.js +++ b/peach-web/static/js/power_menu.js @@ -11,7 +11,6 @@ methods: PEACH_DEVICE.reboot(); PEACH_DEVICE.shutdown(); - PEACH_DEVICE.flashMsg(status, msg); */ @@ -26,7 +25,7 @@ PEACH_DEVICE.reboot = function() { // prevent redirect on button press (default behavior) e.preventDefault(); // write reboot flash message - PEACH_DEVICE.flashMsg("success", "Rebooting the device..."); + PEACH.flashMsg("success", "Rebooting the device..."); // send reboot_device POST request fetch("/api/v1/admin/reboot", { method: "post", @@ -41,7 +40,7 @@ PEACH_DEVICE.reboot = function() { .then( (jsonData) => { console.log(jsonData.msg); // write json response message to ui - PEACH_DEVICE.flashMsg(jsonData.status, jsonData.msg); + PEACH.flashMsg(jsonData.status, jsonData.msg); }) }, false); } @@ -57,7 +56,7 @@ PEACH_DEVICE.shutdown = function() { // prevent form submission (default behavior) e.preventDefault(); // write shutdown flash message - PEACH_DEVICE.flashMsg("success", "Shutting down the device..."); + PEACH.flashMsg("success", "Shutting down the device..."); // send shutdown_device POST request fetch("/api/v1/shutdown", { method: "post", @@ -72,48 +71,13 @@ PEACH_DEVICE.shutdown = function() { .then( (jsonData) => { console.log(jsonData.msg); // write json response message to ui - PEACH_DEVICE.flashMsg(jsonData.status, jsonData.msg); + PEACH.flashMsg(jsonData.status, jsonData.msg); }) }, 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; deviceInstance.reboot(); deviceInstance.shutdown(); diff --git a/peach-web/static/js/reset_password.js b/peach-web/static/js/reset_password.js index bf1c2ec..1c9f431 100644 --- a/peach-web/static/js/reset_password.js +++ b/peach-web/static/js/reset_password.js @@ -2,15 +2,17 @@ * behavioural layer for the `reset_password.html.tera` template, */ +var PEACH_AUTH = {}; + // catch click of 'Save' button and make POST request -PEACH.add = function() { +PEACH_AUTH.resetPassword = function() { document.addEventListener('DOMContentLoaded', function() { document.body.addEventListener('submit', function(e) { // prevent redirect on button press (default behavior) e.preventDefault(); // capture form data var formElement = document.querySelector("form"); - // create form data object from the wifiCreds form element + // create form data object from the changePassword form element var formData = new FormData(formElement); var object = {}; // assign values from form @@ -41,5 +43,5 @@ PEACH.add = function() { }); } -var addInstance = PEACH; -addInstance.add(); +var resetPassInstance = PEACH_AUTH; +resetPassInstance.resetPassword(); diff --git a/peach-web/templates/catchers/not_found.html.tera b/peach-web/templates/catchers/not_found.html.tera index 2d0e80f..4a1f28b 100644 --- a/peach-web/templates/catchers/not_found.html.tera +++ b/peach-web/templates/catchers/not_found.html.tera @@ -1,9 +1,11 @@ {%- extends "nav" -%} {%- block card %} -
-
-

No PeachCloud resource exists for this URL. Please ensure that the URL in the address bar is correct.

-

Click the back arrow in the top-left or the PeachCloud logo at the bottom of your screen to return Home.

-
+
+
+
+

No PeachCloud resource exists for this URL. Please ensure that the URL in the address bar is correct.

+

Click the back arrow in the top-left or the PeachCloud logo at the bottom of your screen to return Home.

+
+
{%- endblock card -%} diff --git a/peach-web/templates/settings/admin/change_password.html.tera b/peach-web/templates/settings/admin/change_password.html.tera index bbe73b5..85d5f30 100644 --- a/peach-web/templates/settings/admin/change_password.html.tera +++ b/peach-web/templates/settings/admin/change_password.html.tera @@ -4,47 +4,22 @@
-
- - -
- -
- - -
- -
- - -
- + + + + + +
- + + Cancel
- Cancel
- {% include "snippets/flash_message" %} - {% include "snippets/noscript" %} -
- {%- endblock card -%} diff --git a/peach-web/templates/settings/admin/forgot_password.html.tera b/peach-web/templates/settings/admin/forgot_password.html.tera index 4cec989..85eff50 100644 --- a/peach-web/templates/settings/admin/forgot_password.html.tera +++ b/peach-web/templates/settings/admin/forgot_password.html.tera @@ -1,25 +1,20 @@ {%- extends "nav" -%} {%- block card %} - +
-

- Click the button below to send a new temporary password which can be used to change your device password. -

- The temporary password will be sent in an SSB private message to the admin of this device. -

- +
+

Click the button below to send a new temporary password which can be used to change your device password. +

+ The temporary password will be sent in an SSB private message to the admin of this device.

+
- +
- {% include "snippets/flash_message" %} - {% include "snippets/noscript" %} - -
{%- endblock card -%} diff --git a/peach-web/templates/settings/admin/menu.html.tera b/peach-web/templates/settings/admin/menu.html.tera index 98716a3..111cb73 100644 --- a/peach-web/templates/settings/admin/menu.html.tera +++ b/peach-web/templates/settings/admin/menu.html.tera @@ -2,12 +2,10 @@ {%- block card %}
- {%- endblock card -%} diff --git a/peach-web/templates/settings/menu.html.tera b/peach-web/templates/settings/menu.html.tera index 43253c8..8a7bdd0 100644 --- a/peach-web/templates/settings/menu.html.tera +++ b/peach-web/templates/settings/menu.html.tera @@ -2,13 +2,11 @@ {%- block card %}
- {%- endblock card -%} diff --git a/peach-web/templates/settings/scuttlebutt.html.tera b/peach-web/templates/settings/scuttlebutt.html.tera index 24587ae..2ceb8de 100644 --- a/peach-web/templates/settings/scuttlebutt.html.tera +++ b/peach-web/templates/settings/scuttlebutt.html.tera @@ -2,19 +2,17 @@ {%- block card %}