summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--.sqlx/query-1dc5f44967ffdee882f4cef32262fd643b452aacca373ee527c978e816115de6.json8
-rw-r--r--.sqlx/query-62c84231c7e9c85dc91d71f6b4f7ee6dae2130c2109fb6f1e47e0990ec395744.json4
-rw-r--r--.sqlx/query-a1114f47abaa2ace231ef61cbd787a2f2a30efc581ea075b7e86ae75458c9014.json46
-rw-r--r--.sqlx/query-adead45e1a6b02d5eabd68b8cf06394a302d288e91f5eedde65db6630021f737.json4
-rw-r--r--.sqlx/query-e452c5ed8bdaec2011d78386cffd2b4739d88b3061931562e07c3e8dd24e6359.json49
-rw-r--r--.sqlx/query-edc4ecf39512caec1076be1b2849b2530be7bcdcbe14362077b3fd47cd711c89.json17
-rw-r--r--Cargo.lock211
-rw-r--r--Cargo.toml8
-rw-r--r--migrations/20231009123228_devices.sql4
-rw-r--r--src/auth.rs43
-rw-r--r--src/db.rs6
-rw-r--r--src/error.rs88
-rw-r--r--src/extractors.rs24
-rw-r--r--src/main.rs60
-rw-r--r--src/routes.rs (renamed from src/routes/mod.rs)0
-rw-r--r--src/routes/device.rs137
-rw-r--r--src/routes/start.rs138
-rw-r--r--src/routes/status.rs79
-rw-r--r--src/services.rs (renamed from src/services/mod.rs)0
-rw-r--r--src/services/ping.rs154
-rw-r--r--src/wol.rs18
22 files changed, 687 insertions, 414 deletions
diff --git a/.gitignore b/.gitignore
index 0fd9734..d8ed708 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,3 +14,6 @@ target/
14config.* 14config.*
15 15
16.idea 16.idea
17
18logs/
19*.log
diff --git a/.sqlx/query-1dc5f44967ffdee882f4cef32262fd643b452aacca373ee527c978e816115de6.json b/.sqlx/query-1dc5f44967ffdee882f4cef32262fd643b452aacca373ee527c978e816115de6.json
index 33d524d..dd85eaa 100644
--- a/.sqlx/query-1dc5f44967ffdee882f4cef32262fd643b452aacca373ee527c978e816115de6.json
+++ b/.sqlx/query-1dc5f44967ffdee882f4cef32262fd643b452aacca373ee527c978e816115de6.json
@@ -11,7 +11,7 @@
11 { 11 {
12 "ordinal": 1, 12 "ordinal": 1,
13 "name": "mac", 13 "name": "mac",
14 "type_info": "Varchar" 14 "type_info": "Macaddr"
15 }, 15 },
16 { 16 {
17 "ordinal": 2, 17 "ordinal": 2,
@@ -21,7 +21,7 @@
21 { 21 {
22 "ordinal": 3, 22 "ordinal": 3,
23 "name": "ip", 23 "name": "ip",
24 "type_info": "Varchar" 24 "type_info": "Inet"
25 }, 25 },
26 { 26 {
27 "ordinal": 4, 27 "ordinal": 4,
@@ -31,9 +31,9 @@
31 ], 31 ],
32 "parameters": { 32 "parameters": {
33 "Left": [ 33 "Left": [
34 "Macaddr",
34 "Varchar", 35 "Varchar",
35 "Varchar", 36 "Inet",
36 "Varchar",
37 "Text" 37 "Text"
38 ] 38 ]
39 }, 39 },
diff --git a/.sqlx/query-62c84231c7e9c85dc91d71f6b4f7ee6dae2130c2109fb6f1e47e0990ec395744.json b/.sqlx/query-62c84231c7e9c85dc91d71f6b4f7ee6dae2130c2109fb6f1e47e0990ec395744.json
index 5ec47e3..905bb51 100644
--- a/.sqlx/query-62c84231c7e9c85dc91d71f6b4f7ee6dae2130c2109fb6f1e47e0990ec395744.json
+++ b/.sqlx/query-62c84231c7e9c85dc91d71f6b4f7ee6dae2130c2109fb6f1e47e0990ec395744.json
@@ -11,7 +11,7 @@
11 { 11 {
12 "ordinal": 1, 12 "ordinal": 1,
13 "name": "mac", 13 "name": "mac",
14 "type_info": "Varchar" 14 "type_info": "Macaddr"
15 }, 15 },
16 { 16 {
17 "ordinal": 2, 17 "ordinal": 2,
@@ -21,7 +21,7 @@
21 { 21 {
22 "ordinal": 3, 22 "ordinal": 3,
23 "name": "ip", 23 "name": "ip",
24 "type_info": "Varchar" 24 "type_info": "Inet"
25 }, 25 },
26 { 26 {
27 "ordinal": 4, 27 "ordinal": 4,
diff --git a/.sqlx/query-a1114f47abaa2ace231ef61cbd787a2f2a30efc581ea075b7e86ae75458c9014.json b/.sqlx/query-a1114f47abaa2ace231ef61cbd787a2f2a30efc581ea075b7e86ae75458c9014.json
new file mode 100644
index 0000000..53608ed
--- /dev/null
+++ b/.sqlx/query-a1114f47abaa2ace231ef61cbd787a2f2a30efc581ea075b7e86ae75458c9014.json
@@ -0,0 +1,46 @@
1{
2 "db_name": "PostgreSQL",
3 "query": "\n SELECT id, mac, broadcast_addr, ip, times\n FROM devices\n WHERE id = $1;\n ",
4 "describe": {
5 "columns": [
6 {
7 "ordinal": 0,
8 "name": "id",
9 "type_info": "Varchar"
10 },
11 {
12 "ordinal": 1,
13 "name": "mac",
14 "type_info": "Macaddr"
15 },
16 {
17 "ordinal": 2,
18 "name": "broadcast_addr",
19 "type_info": "Varchar"
20 },
21 {
22 "ordinal": 3,
23 "name": "ip",
24 "type_info": "Inet"
25 },
26 {
27 "ordinal": 4,
28 "name": "times",
29 "type_info": "Int8Array"
30 }
31 ],
32 "parameters": {
33 "Left": [
34 "Text"
35 ]
36 },
37 "nullable": [
38 false,
39 false,
40 false,
41 false,
42 true
43 ]
44 },
45 "hash": "a1114f47abaa2ace231ef61cbd787a2f2a30efc581ea075b7e86ae75458c9014"
46}
diff --git a/.sqlx/query-adead45e1a6b02d5eabd68b8cf06394a302d288e91f5eedde65db6630021f737.json b/.sqlx/query-adead45e1a6b02d5eabd68b8cf06394a302d288e91f5eedde65db6630021f737.json
index bc4bdd3..d25b12e 100644
--- a/.sqlx/query-adead45e1a6b02d5eabd68b8cf06394a302d288e91f5eedde65db6630021f737.json
+++ b/.sqlx/query-adead45e1a6b02d5eabd68b8cf06394a302d288e91f5eedde65db6630021f737.json
@@ -6,9 +6,9 @@
6 "parameters": { 6 "parameters": {
7 "Left": [ 7 "Left": [
8 "Varchar", 8 "Varchar",
9 "Macaddr",
9 "Varchar", 10 "Varchar",
10 "Varchar", 11 "Inet"
11 "Varchar"
12 ] 12 ]
13 }, 13 },
14 "nullable": [] 14 "nullable": []
diff --git a/.sqlx/query-e452c5ed8bdaec2011d78386cffd2b4739d88b3061931562e07c3e8dd24e6359.json b/.sqlx/query-e452c5ed8bdaec2011d78386cffd2b4739d88b3061931562e07c3e8dd24e6359.json
new file mode 100644
index 0000000..75ec121
--- /dev/null
+++ b/.sqlx/query-e452c5ed8bdaec2011d78386cffd2b4739d88b3061931562e07c3e8dd24e6359.json
@@ -0,0 +1,49 @@
1{
2 "db_name": "PostgreSQL",
3 "query": "\n UPDATE devices\n SET mac = $1, broadcast_addr = $2, ip = $3 WHERE id = $4\n RETURNING id, mac, broadcast_addr, ip, times;\n ",
4 "describe": {
5 "columns": [
6 {
7 "ordinal": 0,
8 "name": "id",
9 "type_info": "Varchar"
10 },
11 {
12 "ordinal": 1,
13 "name": "mac",
14 "type_info": "Macaddr"
15 },
16 {
17 "ordinal": 2,
18 "name": "broadcast_addr",
19 "type_info": "Varchar"
20 },
21 {
22 "ordinal": 3,
23 "name": "ip",
24 "type_info": "Inet"
25 },
26 {
27 "ordinal": 4,
28 "name": "times",
29 "type_info": "Int8Array"
30 }
31 ],
32 "parameters": {
33 "Left": [
34 "Macaddr",
35 "Varchar",
36 "Inet",
37 "Text"
38 ]
39 },
40 "nullable": [
41 false,
42 false,
43 false,
44 false,
45 true
46 ]
47 },
48 "hash": "e452c5ed8bdaec2011d78386cffd2b4739d88b3061931562e07c3e8dd24e6359"
49}
diff --git a/.sqlx/query-edc4ecf39512caec1076be1b2849b2530be7bcdcbe14362077b3fd47cd711c89.json b/.sqlx/query-edc4ecf39512caec1076be1b2849b2530be7bcdcbe14362077b3fd47cd711c89.json
new file mode 100644
index 0000000..2b17c9b
--- /dev/null
+++ b/.sqlx/query-edc4ecf39512caec1076be1b2849b2530be7bcdcbe14362077b3fd47cd711c89.json
@@ -0,0 +1,17 @@
1{
2 "db_name": "PostgreSQL",
3 "query": "\n INSERT INTO devices (id, mac, broadcast_addr, ip)\n VALUES ($1, $2, $3, $4);\n ",
4 "describe": {
5 "columns": [],
6 "parameters": {
7 "Left": [
8 "Varchar",
9 "Macaddr",
10 "Varchar",
11 "Inet"
12 ]
13 },
14 "nullable": []
15 },
16 "hash": "edc4ecf39512caec1076be1b2849b2530be7bcdcbe14362077b3fd47cd711c89"
17}
diff --git a/Cargo.lock b/Cargo.lock
index f95052b..fcbf58a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -19,9 +19,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
19 19
20[[package]] 20[[package]]
21name = "ahash" 21name = "ahash"
22version = "0.8.8" 22version = "0.8.9"
23source = "registry+https://github.com/rust-lang/crates.io-index" 23source = "registry+https://github.com/rust-lang/crates.io-index"
24checksum = "42cd52102d3df161c77a887b608d7a4897d7cc112886a9537b738a887a03aaff" 24checksum = "d713b3834d76b85304d4d525563c1276e2e30dc97cc67bfb4585a4a29fc2c89f"
25dependencies = [ 25dependencies = [
26 "cfg-if", 26 "cfg-if",
27 "getrandom", 27 "getrandom",
@@ -53,7 +53,7 @@ checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9"
53dependencies = [ 53dependencies = [
54 "proc-macro2", 54 "proc-macro2",
55 "quote", 55 "quote",
56 "syn 2.0.48", 56 "syn 2.0.50",
57] 57]
58 58
59[[package]] 59[[package]]
@@ -71,7 +71,7 @@ version = "0.1.2"
71source = "registry+https://github.com/rust-lang/crates.io-index" 71source = "registry+https://github.com/rust-lang/crates.io-index"
72checksum = "edcdbedc2236483ab103a53415653d6b4442ea6141baf1ffa85df29635e88436" 72checksum = "edcdbedc2236483ab103a53415653d6b4442ea6141baf1ffa85df29635e88436"
73dependencies = [ 73dependencies = [
74 "nix", 74 "nix 0.27.1",
75 "rand", 75 "rand",
76] 76]
77 77
@@ -148,7 +148,7 @@ dependencies = [
148 "heck", 148 "heck",
149 "proc-macro2", 149 "proc-macro2",
150 "quote", 150 "quote",
151 "syn 2.0.48", 151 "syn 2.0.50",
152] 152]
153 153
154[[package]] 154[[package]]
@@ -216,12 +216,9 @@ checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
216 216
217[[package]] 217[[package]]
218name = "cc" 218name = "cc"
219version = "1.0.83" 219version = "1.0.88"
220source = "registry+https://github.com/rust-lang/crates.io-index" 220source = "registry+https://github.com/rust-lang/crates.io-index"
221checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" 221checksum = "02f341c093d19155a6e41631ce5971aac4e9a868262212153124c15fa22d1cdc"
222dependencies = [
223 "libc",
224]
225 222
226[[package]] 223[[package]]
227name = "cfg-if" 224name = "cfg-if"
@@ -336,6 +333,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
336checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" 333checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
337 334
338[[package]] 335[[package]]
336name = "crossbeam-channel"
337version = "0.5.11"
338source = "registry+https://github.com/rust-lang/crates.io-index"
339checksum = "176dc175b78f56c0f321911d9c8eb2b77a78a4860b9c19db83835fea1a46649b"
340dependencies = [
341 "crossbeam-utils",
342]
343
344[[package]]
339name = "crossbeam-queue" 345name = "crossbeam-queue"
340version = "0.3.11" 346version = "0.3.11"
341source = "registry+https://github.com/rust-lang/crates.io-index" 347source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -682,9 +688,9 @@ dependencies = [
682 688
683[[package]] 689[[package]]
684name = "hermit-abi" 690name = "hermit-abi"
685version = "0.3.5" 691version = "0.3.8"
686source = "registry+https://github.com/rust-lang/crates.io-index" 692source = "registry+https://github.com/rust-lang/crates.io-index"
687checksum = "d0c62115964e08cb8039170eb33c1d0e2388a256930279edca206fff675f82c3" 693checksum = "379dada1584ad501b383485dd706b8afb7a70fcbc7f4da7d780638a5a6124a60"
688 694
689[[package]] 695[[package]]
690name = "hex" 696name = "hex"
@@ -767,9 +773,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
767 773
768[[package]] 774[[package]]
769name = "hyper" 775name = "hyper"
770version = "1.1.0" 776version = "1.2.0"
771source = "registry+https://github.com/rust-lang/crates.io-index" 777source = "registry+https://github.com/rust-lang/crates.io-index"
772checksum = "fb5aa53871fc917b1a9ed87b683a5d86db645e23acb32c2e0785a353e522fb75" 778checksum = "186548d73ac615b32a73aafe38fb4f56c0d340e110e5a200bcadbaf2e199263a"
773dependencies = [ 779dependencies = [
774 "bytes", 780 "bytes",
775 "futures-channel", 781 "futures-channel",
@@ -781,6 +787,7 @@ dependencies = [
781 "httpdate", 787 "httpdate",
782 "itoa", 788 "itoa",
783 "pin-project-lite", 789 "pin-project-lite",
790 "smallvec",
784 "tokio", 791 "tokio",
785] 792]
786 793
@@ -827,6 +834,15 @@ dependencies = [
827] 834]
828 835
829[[package]] 836[[package]]
837name = "ipnetwork"
838version = "0.20.0"
839source = "registry+https://github.com/rust-lang/crates.io-index"
840checksum = "bf466541e9d546596ee94f9f69590f89473455f88372423e0008fc1a7daf100e"
841dependencies = [
842 "serde",
843]
844
845[[package]]
830name = "itertools" 846name = "itertools"
831version = "0.12.1" 847version = "0.12.1"
832source = "registry+https://github.com/rust-lang/crates.io-index" 848source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -913,6 +929,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
913checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" 929checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
914 930
915[[package]] 931[[package]]
932name = "mac_address"
933version = "1.1.5"
934source = "registry+https://github.com/rust-lang/crates.io-index"
935checksum = "4863ee94f19ed315bf3bc00299338d857d4b5bc856af375cc97d237382ad3856"
936dependencies = [
937 "nix 0.23.2",
938 "serde",
939 "winapi",
940]
941
942[[package]]
916name = "matchers" 943name = "matchers"
917version = "0.1.0" 944version = "0.1.0"
918source = "registry+https://github.com/rust-lang/crates.io-index" 945source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -944,6 +971,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
944checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" 971checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
945 972
946[[package]] 973[[package]]
974name = "memoffset"
975version = "0.6.5"
976source = "registry+https://github.com/rust-lang/crates.io-index"
977checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
978dependencies = [
979 "autocfg",
980]
981
982[[package]]
947name = "mime" 983name = "mime"
948version = "0.3.17" 984version = "0.3.17"
949source = "registry+https://github.com/rust-lang/crates.io-index" 985source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -977,6 +1013,19 @@ dependencies = [
977 1013
978[[package]] 1014[[package]]
979name = "nix" 1015name = "nix"
1016version = "0.23.2"
1017source = "registry+https://github.com/rust-lang/crates.io-index"
1018checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c"
1019dependencies = [
1020 "bitflags 1.3.2",
1021 "cc",
1022 "cfg-if",
1023 "libc",
1024 "memoffset",
1025]
1026
1027[[package]]
1028name = "nix"
980version = "0.27.1" 1029version = "0.27.1"
981source = "registry+https://github.com/rust-lang/crates.io-index" 1030source = "registry+https://github.com/rust-lang/crates.io-index"
982checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" 1031checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053"
@@ -1077,9 +1126,9 @@ dependencies = [
1077 1126
1078[[package]] 1127[[package]]
1079name = "num_threads" 1128name = "num_threads"
1080version = "0.1.6" 1129version = "0.1.7"
1081source = "registry+https://github.com/rust-lang/crates.io-index" 1130source = "registry+https://github.com/rust-lang/crates.io-index"
1082checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" 1131checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
1083dependencies = [ 1132dependencies = [
1084 "libc", 1133 "libc",
1085] 1134]
@@ -1202,7 +1251,7 @@ dependencies = [
1202 "pest_meta", 1251 "pest_meta",
1203 "proc-macro2", 1252 "proc-macro2",
1204 "quote", 1253 "quote",
1205 "syn 2.0.48", 1254 "syn 2.0.50",
1206] 1255]
1207 1256
1208[[package]] 1257[[package]]
@@ -1233,7 +1282,7 @@ checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690"
1233dependencies = [ 1282dependencies = [
1234 "proc-macro2", 1283 "proc-macro2",
1235 "quote", 1284 "quote",
1236 "syn 2.0.48", 1285 "syn 2.0.50",
1237] 1286]
1238 1287
1239[[package]] 1288[[package]]
@@ -1271,9 +1320,9 @@ dependencies = [
1271 1320
1272[[package]] 1321[[package]]
1273name = "pkg-config" 1322name = "pkg-config"
1274version = "0.3.29" 1323version = "0.3.30"
1275source = "registry+https://github.com/rust-lang/crates.io-index" 1324source = "registry+https://github.com/rust-lang/crates.io-index"
1276checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb" 1325checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
1277 1326
1278[[package]] 1327[[package]]
1279name = "pnet_base" 1328name = "pnet_base"
@@ -1499,9 +1548,9 @@ checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
1499 1548
1500[[package]] 1549[[package]]
1501name = "ryu" 1550name = "ryu"
1502version = "1.0.16" 1551version = "1.0.17"
1503source = "registry+https://github.com/rust-lang/crates.io-index" 1552source = "registry+https://github.com/rust-lang/crates.io-index"
1504checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" 1553checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
1505 1554
1506[[package]] 1555[[package]]
1507name = "scopeguard" 1556name = "scopeguard"
@@ -1511,29 +1560,29 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
1511 1560
1512[[package]] 1561[[package]]
1513name = "serde" 1562name = "serde"
1514version = "1.0.196" 1563version = "1.0.197"
1515source = "registry+https://github.com/rust-lang/crates.io-index" 1564source = "registry+https://github.com/rust-lang/crates.io-index"
1516checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" 1565checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2"
1517dependencies = [ 1566dependencies = [
1518 "serde_derive", 1567 "serde_derive",
1519] 1568]
1520 1569
1521[[package]] 1570[[package]]
1522name = "serde_derive" 1571name = "serde_derive"
1523version = "1.0.196" 1572version = "1.0.197"
1524source = "registry+https://github.com/rust-lang/crates.io-index" 1573source = "registry+https://github.com/rust-lang/crates.io-index"
1525checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" 1574checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
1526dependencies = [ 1575dependencies = [
1527 "proc-macro2", 1576 "proc-macro2",
1528 "quote", 1577 "quote",
1529 "syn 2.0.48", 1578 "syn 2.0.50",
1530] 1579]
1531 1580
1532[[package]] 1581[[package]]
1533name = "serde_json" 1582name = "serde_json"
1534version = "1.0.113" 1583version = "1.0.114"
1535source = "registry+https://github.com/rust-lang/crates.io-index" 1584source = "registry+https://github.com/rust-lang/crates.io-index"
1536checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79" 1585checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0"
1537dependencies = [ 1586dependencies = [
1538 "itoa", 1587 "itoa",
1539 "ryu", 1588 "ryu",
@@ -1629,12 +1678,12 @@ checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
1629 1678
1630[[package]] 1679[[package]]
1631name = "socket2" 1680name = "socket2"
1632version = "0.5.5" 1681version = "0.5.6"
1633source = "registry+https://github.com/rust-lang/crates.io-index" 1682source = "registry+https://github.com/rust-lang/crates.io-index"
1634checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" 1683checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871"
1635dependencies = [ 1684dependencies = [
1636 "libc", 1685 "libc",
1637 "windows-sys 0.48.0", 1686 "windows-sys 0.52.0",
1638] 1687]
1639 1688
1640[[package]] 1689[[package]]
@@ -1709,7 +1758,9 @@ dependencies = [
1709 "hashlink", 1758 "hashlink",
1710 "hex", 1759 "hex",
1711 "indexmap", 1760 "indexmap",
1761 "ipnetwork",
1712 "log", 1762 "log",
1763 "mac_address",
1713 "memchr", 1764 "memchr",
1714 "once_cell", 1765 "once_cell",
1715 "paste", 1766 "paste",
@@ -1829,8 +1880,10 @@ dependencies = [
1829 "hkdf", 1880 "hkdf",
1830 "hmac", 1881 "hmac",
1831 "home", 1882 "home",
1883 "ipnetwork",
1832 "itoa", 1884 "itoa",
1833 "log", 1885 "log",
1886 "mac_address",
1834 "md-5", 1887 "md-5",
1835 "memchr", 1888 "memchr",
1836 "once_cell", 1889 "once_cell",
@@ -1916,9 +1969,9 @@ dependencies = [
1916 1969
1917[[package]] 1970[[package]]
1918name = "syn" 1971name = "syn"
1919version = "2.0.48" 1972version = "2.0.50"
1920source = "registry+https://github.com/rust-lang/crates.io-index" 1973source = "registry+https://github.com/rust-lang/crates.io-index"
1921checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" 1974checksum = "74f1bdc9872430ce9b75da68329d1c1746faf50ffac5f19e02b71e37ff881ffb"
1922dependencies = [ 1975dependencies = [
1923 "proc-macro2", 1976 "proc-macro2",
1924 "quote", 1977 "quote",
@@ -1960,14 +2013,14 @@ checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81"
1960dependencies = [ 2013dependencies = [
1961 "proc-macro2", 2014 "proc-macro2",
1962 "quote", 2015 "quote",
1963 "syn 2.0.48", 2016 "syn 2.0.50",
1964] 2017]
1965 2018
1966[[package]] 2019[[package]]
1967name = "thread_local" 2020name = "thread_local"
1968version = "1.1.7" 2021version = "1.1.8"
1969source = "registry+https://github.com/rust-lang/crates.io-index" 2022source = "registry+https://github.com/rust-lang/crates.io-index"
1970checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" 2023checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
1971dependencies = [ 2024dependencies = [
1972 "cfg-if", 2025 "cfg-if",
1973 "once_cell", 2026 "once_cell",
@@ -2055,7 +2108,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
2055dependencies = [ 2108dependencies = [
2056 "proc-macro2", 2109 "proc-macro2",
2057 "quote", 2110 "quote",
2058 "syn 2.0.48", 2111 "syn 2.0.50",
2059] 2112]
2060 2113
2061[[package]] 2114[[package]]
@@ -2118,9 +2171,9 @@ dependencies = [
2118 2171
2119[[package]] 2172[[package]]
2120name = "toml_edit" 2173name = "toml_edit"
2121version = "0.22.4" 2174version = "0.22.6"
2122source = "registry+https://github.com/rust-lang/crates.io-index" 2175source = "registry+https://github.com/rust-lang/crates.io-index"
2123checksum = "0c9ffdf896f8daaabf9b66ba8e77ea1ed5ed0f72821b398aba62352e95062951" 2176checksum = "2c1b5fd4128cc8d3e0cb74d4ed9a9cc7c7284becd4df68f5f940e1ad123606f6"
2124dependencies = [ 2177dependencies = [
2125 "indexmap", 2178 "indexmap",
2126 "serde", 2179 "serde",
@@ -2170,6 +2223,18 @@ dependencies = [
2170] 2223]
2171 2224
2172[[package]] 2225[[package]]
2226name = "tracing-appender"
2227version = "0.2.3"
2228source = "registry+https://github.com/rust-lang/crates.io-index"
2229checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf"
2230dependencies = [
2231 "crossbeam-channel",
2232 "thiserror",
2233 "time",
2234 "tracing-subscriber",
2235]
2236
2237[[package]]
2173name = "tracing-attributes" 2238name = "tracing-attributes"
2174version = "0.1.27" 2239version = "0.1.27"
2175source = "registry+https://github.com/rust-lang/crates.io-index" 2240source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2177,7 +2242,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
2177dependencies = [ 2242dependencies = [
2178 "proc-macro2", 2243 "proc-macro2",
2179 "quote", 2244 "quote",
2180 "syn 2.0.48", 2245 "syn 2.0.50",
2181] 2246]
2182 2247
2183[[package]] 2248[[package]]
@@ -2275,9 +2340,9 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
2275 2340
2276[[package]] 2341[[package]]
2277name = "unicode-normalization" 2342name = "unicode-normalization"
2278version = "0.1.22" 2343version = "0.1.23"
2279source = "registry+https://github.com/rust-lang/crates.io-index" 2344source = "registry+https://github.com/rust-lang/crates.io-index"
2280checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" 2345checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5"
2281dependencies = [ 2346dependencies = [
2282 "tinyvec", 2347 "tinyvec",
2283] 2348]
@@ -2353,20 +2418,24 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
2353 2418
2354[[package]] 2419[[package]]
2355name = "webol" 2420name = "webol"
2356version = "0.3.1" 2421version = "0.3.2"
2357dependencies = [ 2422dependencies = [
2358 "axum", 2423 "axum",
2359 "axum-macros", 2424 "axum-macros",
2360 "color-eyre", 2425 "color-eyre",
2361 "config", 2426 "config",
2362 "dashmap", 2427 "dashmap",
2428 "ipnetwork",
2429 "mac_address",
2363 "serde", 2430 "serde",
2364 "serde_json", 2431 "serde_json",
2365 "sqlx", 2432 "sqlx",
2366 "surge-ping", 2433 "surge-ping",
2434 "thiserror",
2367 "time", 2435 "time",
2368 "tokio", 2436 "tokio",
2369 "tracing", 2437 "tracing",
2438 "tracing-appender",
2370 "tracing-subscriber", 2439 "tracing-subscriber",
2371 "uuid", 2440 "uuid",
2372] 2441]
@@ -2414,7 +2483,7 @@ version = "0.52.0"
2414source = "registry+https://github.com/rust-lang/crates.io-index" 2483source = "registry+https://github.com/rust-lang/crates.io-index"
2415checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 2484checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
2416dependencies = [ 2485dependencies = [
2417 "windows-targets 0.52.0", 2486 "windows-targets 0.52.3",
2418] 2487]
2419 2488
2420[[package]] 2489[[package]]
@@ -2434,17 +2503,17 @@ dependencies = [
2434 2503
2435[[package]] 2504[[package]]
2436name = "windows-targets" 2505name = "windows-targets"
2437version = "0.52.0" 2506version = "0.52.3"
2438source = "registry+https://github.com/rust-lang/crates.io-index" 2507source = "registry+https://github.com/rust-lang/crates.io-index"
2439checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" 2508checksum = "d380ba1dc7187569a8a9e91ed34b8ccfc33123bbacb8c0aed2d1ad7f3ef2dc5f"
2440dependencies = [ 2509dependencies = [
2441 "windows_aarch64_gnullvm 0.52.0", 2510 "windows_aarch64_gnullvm 0.52.3",
2442 "windows_aarch64_msvc 0.52.0", 2511 "windows_aarch64_msvc 0.52.3",
2443 "windows_i686_gnu 0.52.0", 2512 "windows_i686_gnu 0.52.3",
2444 "windows_i686_msvc 0.52.0", 2513 "windows_i686_msvc 0.52.3",
2445 "windows_x86_64_gnu 0.52.0", 2514 "windows_x86_64_gnu 0.52.3",
2446 "windows_x86_64_gnullvm 0.52.0", 2515 "windows_x86_64_gnullvm 0.52.3",
2447 "windows_x86_64_msvc 0.52.0", 2516 "windows_x86_64_msvc 0.52.3",
2448] 2517]
2449 2518
2450[[package]] 2519[[package]]
@@ -2455,9 +2524,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
2455 2524
2456[[package]] 2525[[package]]
2457name = "windows_aarch64_gnullvm" 2526name = "windows_aarch64_gnullvm"
2458version = "0.52.0" 2527version = "0.52.3"
2459source = "registry+https://github.com/rust-lang/crates.io-index" 2528source = "registry+https://github.com/rust-lang/crates.io-index"
2460checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" 2529checksum = "68e5dcfb9413f53afd9c8f86e56a7b4d86d9a2fa26090ea2dc9e40fba56c6ec6"
2461 2530
2462[[package]] 2531[[package]]
2463name = "windows_aarch64_msvc" 2532name = "windows_aarch64_msvc"
@@ -2467,9 +2536,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
2467 2536
2468[[package]] 2537[[package]]
2469name = "windows_aarch64_msvc" 2538name = "windows_aarch64_msvc"
2470version = "0.52.0" 2539version = "0.52.3"
2471source = "registry+https://github.com/rust-lang/crates.io-index" 2540source = "registry+https://github.com/rust-lang/crates.io-index"
2472checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" 2541checksum = "8dab469ebbc45798319e69eebf92308e541ce46760b49b18c6b3fe5e8965b30f"
2473 2542
2474[[package]] 2543[[package]]
2475name = "windows_i686_gnu" 2544name = "windows_i686_gnu"
@@ -2479,9 +2548,9 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
2479 2548
2480[[package]] 2549[[package]]
2481name = "windows_i686_gnu" 2550name = "windows_i686_gnu"
2482version = "0.52.0" 2551version = "0.52.3"
2483source = "registry+https://github.com/rust-lang/crates.io-index" 2552source = "registry+https://github.com/rust-lang/crates.io-index"
2484checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" 2553checksum = "2a4e9b6a7cac734a8b4138a4e1044eac3404d8326b6c0f939276560687a033fb"
2485 2554
2486[[package]] 2555[[package]]
2487name = "windows_i686_msvc" 2556name = "windows_i686_msvc"
@@ -2491,9 +2560,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
2491 2560
2492[[package]] 2561[[package]]
2493name = "windows_i686_msvc" 2562name = "windows_i686_msvc"
2494version = "0.52.0" 2563version = "0.52.3"
2495source = "registry+https://github.com/rust-lang/crates.io-index" 2564source = "registry+https://github.com/rust-lang/crates.io-index"
2496checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" 2565checksum = "28b0ec9c422ca95ff34a78755cfa6ad4a51371da2a5ace67500cf7ca5f232c58"
2497 2566
2498[[package]] 2567[[package]]
2499name = "windows_x86_64_gnu" 2568name = "windows_x86_64_gnu"
@@ -2503,9 +2572,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
2503 2572
2504[[package]] 2573[[package]]
2505name = "windows_x86_64_gnu" 2574name = "windows_x86_64_gnu"
2506version = "0.52.0" 2575version = "0.52.3"
2507source = "registry+https://github.com/rust-lang/crates.io-index" 2576source = "registry+https://github.com/rust-lang/crates.io-index"
2508checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" 2577checksum = "704131571ba93e89d7cd43482277d6632589b18ecf4468f591fbae0a8b101614"
2509 2578
2510[[package]] 2579[[package]]
2511name = "windows_x86_64_gnullvm" 2580name = "windows_x86_64_gnullvm"
@@ -2515,9 +2584,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
2515 2584
2516[[package]] 2585[[package]]
2517name = "windows_x86_64_gnullvm" 2586name = "windows_x86_64_gnullvm"
2518version = "0.52.0" 2587version = "0.52.3"
2519source = "registry+https://github.com/rust-lang/crates.io-index" 2588source = "registry+https://github.com/rust-lang/crates.io-index"
2520checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" 2589checksum = "42079295511643151e98d61c38c0acc444e52dd42ab456f7ccfd5152e8ecf21c"
2521 2590
2522[[package]] 2591[[package]]
2523name = "windows_x86_64_msvc" 2592name = "windows_x86_64_msvc"
@@ -2527,15 +2596,15 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
2527 2596
2528[[package]] 2597[[package]]
2529name = "windows_x86_64_msvc" 2598name = "windows_x86_64_msvc"
2530version = "0.52.0" 2599version = "0.52.3"
2531source = "registry+https://github.com/rust-lang/crates.io-index" 2600source = "registry+https://github.com/rust-lang/crates.io-index"
2532checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" 2601checksum = "0770833d60a970638e989b3fa9fd2bb1aaadcf88963d1659fd7d9990196ed2d6"
2533 2602
2534[[package]] 2603[[package]]
2535name = "winnow" 2604name = "winnow"
2536version = "0.5.39" 2605version = "0.6.2"
2537source = "registry+https://github.com/rust-lang/crates.io-index" 2606source = "registry+https://github.com/rust-lang/crates.io-index"
2538checksum = "5389a154b01683d28c77f8f68f49dea75f0a4da32557a58f68ee51ebba472d29" 2607checksum = "7a4191c47f15cc3ec71fcb4913cb83d58def65dd3787610213c649283b5ce178"
2539dependencies = [ 2608dependencies = [
2540 "memchr", 2609 "memchr",
2541] 2610]
@@ -2566,7 +2635,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
2566dependencies = [ 2635dependencies = [
2567 "proc-macro2", 2636 "proc-macro2",
2568 "quote", 2637 "quote",
2569 "syn 2.0.48", 2638 "syn 2.0.50",
2570] 2639]
2571 2640
2572[[package]] 2641[[package]]
diff --git a/Cargo.toml b/Cargo.toml
index 0fb250a..8a86115 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
1[package] 1[package]
2name = "webol" 2name = "webol"
3version = "0.3.1" 3version = "0.3.2"
4edition = "2021" 4edition = "2021"
5 5
6# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 6# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -14,9 +14,13 @@ time = { version = "0.3", features = ["macros"] }
14serde = { version = "1.0", features = ["derive"] } 14serde = { version = "1.0", features = ["derive"] }
15serde_json = "1.0" 15serde_json = "1.0"
16config = "0.14" 16config = "0.14"
17sqlx = { version = "0.7", features = ["postgres", "runtime-tokio"]} 17sqlx = { version = "0.7", features = ["postgres", "runtime-tokio", "ipnetwork", "mac_address"]}
18surge-ping = "0.8" 18surge-ping = "0.8"
19axum-macros = "0.4" 19axum-macros = "0.4"
20uuid = { version = "1.6", features = ["v4", "fast-rng"] } 20uuid = { version = "1.6", features = ["v4", "fast-rng"] }
21dashmap = "5.5" 21dashmap = "5.5"
22color-eyre = "0.6" 22color-eyre = "0.6"
23thiserror = "1.0"
24ipnetwork = "0.20.0"
25mac_address = { version = "1.1.5", features = ["serde"] }
26tracing-appender = "0.2.3"
diff --git a/migrations/20231009123228_devices.sql b/migrations/20231009123228_devices.sql
index d36946c..6983ada 100644
--- a/migrations/20231009123228_devices.sql
+++ b/migrations/20231009123228_devices.sql
@@ -2,8 +2,8 @@
2CREATE TABLE IF NOT EXISTS "devices" 2CREATE TABLE IF NOT EXISTS "devices"
3( 3(
4 "id" VARCHAR(255) PRIMARY KEY NOT NULL, 4 "id" VARCHAR(255) PRIMARY KEY NOT NULL,
5 "mac" VARCHAR(17) NOT NULL, 5 "mac" MACADDR NOT NULL,
6 "broadcast_addr" VARCHAR(39) NOT NULL, 6 "broadcast_addr" VARCHAR(39) NOT NULL,
7 "ip" VARCHAR(39) NOT NULL, 7 "ip" INET NOT NULL,
8 "times" BIGINT[] 8 "times" BIGINT[]
9) 9)
diff --git a/src/auth.rs b/src/auth.rs
deleted file mode 100644
index feca652..0000000
--- a/src/auth.rs
+++ /dev/null
@@ -1,43 +0,0 @@
1use axum::http::{StatusCode, HeaderValue};
2use axum::http::header::ToStrError;
3use tracing::{debug, error, trace};
4use crate::auth::Error::{MissingSecret, WrongSecret};
5use crate::config::Config;
6
7pub fn auth(config: &Config, secret: Option<&HeaderValue>) -> Result<bool, Error> {
8 debug!("auth request with secret {:?}", secret);
9 if let Some(value) = secret {
10 trace!("value exists");
11 let key = &config.apikey;
12 if value.to_str().map_err(Error::HeaderToStr)? == key.as_str() {
13 debug!("successful auth");
14 Ok(true)
15 } else {
16 debug!("unsuccessful auth (wrong secret)");
17 Err(WrongSecret)
18 }
19 } else {
20 debug!("unsuccessful auth (no secret)");
21 Err(MissingSecret)
22 }
23}
24
25#[derive(Debug)]
26pub enum Error {
27 WrongSecret,
28 MissingSecret,
29 HeaderToStr(ToStrError)
30}
31
32impl Error {
33 pub fn get(self) -> (StatusCode, &'static str) {
34 match self {
35 Self::WrongSecret => (StatusCode::UNAUTHORIZED, "Wrong credentials"),
36 Self::MissingSecret => (StatusCode::BAD_REQUEST, "Missing credentials"),
37 Self::HeaderToStr(err) => {
38 error!("server error: {}", err.to_string());
39 (StatusCode::INTERNAL_SERVER_ERROR, "Server Error")
40 },
41 }
42 }
43}
diff --git a/src/db.rs b/src/db.rs
index 489a000..47e907d 100644
--- a/src/db.rs
+++ b/src/db.rs
@@ -1,13 +1,13 @@
1use serde::Serialize; 1use serde::Serialize;
2use sqlx::{PgPool, postgres::PgPoolOptions}; 2use sqlx::{PgPool, postgres::PgPoolOptions, types::{ipnetwork::IpNetwork, mac_address::MacAddress}};
3use tracing::{debug, info}; 3use tracing::{debug, info};
4 4
5#[derive(Serialize, Debug)] 5#[derive(Serialize, Debug)]
6pub struct Device { 6pub struct Device {
7 pub id: String, 7 pub id: String,
8 pub mac: String, 8 pub mac: MacAddress,
9 pub broadcast_addr: String, 9 pub broadcast_addr: String,
10 pub ip: String, 10 pub ip: IpNetwork,
11 pub times: Option<Vec<i64>> 11 pub times: Option<Vec<i64>>
12} 12}
13 13
diff --git a/src/error.rs b/src/error.rs
index 56d6c52..513b51b 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -1,44 +1,80 @@
1use std::io; 1use ::ipnetwork::IpNetworkError;
2use axum::http::header::ToStrError;
2use axum::http::StatusCode; 3use axum::http::StatusCode;
3use axum::Json;
4use axum::response::{IntoResponse, Response}; 4use axum::response::{IntoResponse, Response};
5use axum::Json;
6use mac_address::MacParseError;
5use serde_json::json; 7use serde_json::json;
8use std::io;
6use tracing::error; 9use tracing::error;
7use crate::auth::Error as AuthError;
8 10
9#[derive(Debug)] 11#[derive(Debug, thiserror::Error)]
10pub enum Error { 12pub enum Error {
11 Generic, 13 #[error("db: {source}")]
12 Auth(AuthError), 14 Db {
13 DB(sqlx::Error), 15 #[from]
14 IpParse(<std::net::IpAddr as std::str::FromStr>::Err), 16 source: sqlx::Error,
15 BufferParse(std::num::ParseIntError), 17 },
16 Broadcast(io::Error), 18
19 #[error("buffer parse: {source}")]
20 ParseInt {
21 #[from]
22 source: std::num::ParseIntError,
23 },
24
25 #[error("header parse: {source}")]
26 ParseHeader {
27 #[from]
28 source: ToStrError,
29 },
30
31 #[error("string parse: {source}")]
32 IpParse {
33 #[from]
34 source: IpNetworkError,
35 },
36
37 #[error("mac parse: {source}")]
38 MacParse {
39 #[from]
40 source: MacParseError,
41 },
42
43 #[error("io: {source}")]
44 Io {
45 #[from]
46 source: io::Error,
47 },
17} 48}
18 49
19impl IntoResponse for Error { 50impl IntoResponse for Error {
20 fn into_response(self) -> Response { 51 fn into_response(self) -> Response {
52 error!("{}", self.to_string());
21 let (status, error_message) = match self { 53 let (status, error_message) = match self {
22 Self::Auth(err) => { 54 Self::Db { source } => {
23 err.get() 55 error!("{source}");
24 }, 56 (StatusCode::INTERNAL_SERVER_ERROR, "Server Error")
25 Self::Generic => (StatusCode::INTERNAL_SERVER_ERROR, ""), 57 }
26 Self::IpParse(err) => { 58 Self::Io { source } => {
27 error!("server error: {}", err.to_string()); 59 error!("{source}");
60 (StatusCode::INTERNAL_SERVER_ERROR, "Server Error")
61 }
62 Self::ParseHeader { source } => {
63 error!("{source}");
28 (StatusCode::INTERNAL_SERVER_ERROR, "Server Error") 64 (StatusCode::INTERNAL_SERVER_ERROR, "Server Error")
29 }, 65 }
30 Self::DB(err) => { 66 Self::ParseInt { source } => {
31 error!("server error: {}", err.to_string()); 67 error!("{source}");
32 (StatusCode::INTERNAL_SERVER_ERROR, "Server Error") 68 (StatusCode::INTERNAL_SERVER_ERROR, "Server Error")
33 }, 69 }
34 Self::Broadcast(err) => { 70 Self::MacParse { source } => {
35 error!("server error: {}", err.to_string()); 71 error!("{source}");
36 (StatusCode::INTERNAL_SERVER_ERROR, "Server Error") 72 (StatusCode::INTERNAL_SERVER_ERROR, "Server Error")
37 }, 73 }
38 Self::BufferParse(err) => { 74 Self::IpParse { source } => {
39 error!("server error: {}", err.to_string()); 75 error!("{source}");
40 (StatusCode::INTERNAL_SERVER_ERROR, "Server Error") 76 (StatusCode::INTERNAL_SERVER_ERROR, "Server Error")
41 }, 77 }
42 }; 78 };
43 let body = Json(json!({ 79 let body = Json(json!({
44 "error": error_message, 80 "error": error_message,
diff --git a/src/extractors.rs b/src/extractors.rs
new file mode 100644
index 0000000..4d441e9
--- /dev/null
+++ b/src/extractors.rs
@@ -0,0 +1,24 @@
1use axum::{
2 extract::{Request, State},
3 http::{HeaderMap, StatusCode},
4 middleware::Next,
5 response::Response,
6};
7
8use crate::AppState;
9
10pub async fn auth(
11 State(state): State<AppState>,
12 headers: HeaderMap,
13 request: Request,
14 next: Next,
15) -> Result<Response, StatusCode> {
16 let secret = headers.get("authorization");
17 match secret {
18 Some(token) if token == state.config.apikey.as_str() => {
19 let response = next.run(request).await;
20 Ok(response)
21 }
22 _ => Err(StatusCode::UNAUTHORIZED),
23 }
24}
diff --git a/src/main.rs b/src/main.rs
index 4ef129b..d17984f 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,42 +1,44 @@
1use std::env;
2use std::sync::Arc;
3use axum::{Router, routing::post};
4use axum::routing::{get, put};
5use dashmap::DashMap;
6use sqlx::PgPool;
7use time::util::local_offset;
8use tokio::sync::broadcast::{channel, Sender};
9use tracing::{info, level_filters::LevelFilter};
10use tracing_subscriber::{EnvFilter, fmt::{self, time::LocalTime}, prelude::*};
11use crate::config::Config; 1use crate::config::Config;
12use crate::db::init_db_pool; 2use crate::db::init_db_pool;
13use crate::routes::device; 3use crate::routes::device;
14use crate::routes::start::start; 4use crate::routes::start::start;
15use crate::routes::status::status; 5use crate::routes::status::status;
16use crate::services::ping::{BroadcastCommands, StatusMap}; 6use crate::services::ping::StatusMap;
7use axum::middleware::from_fn_with_state;
8use axum::routing::{get, put};
9use axum::{routing::post, Router};
10use dashmap::DashMap;
11use services::ping::BroadcastCommand;
12use sqlx::PgPool;
13use std::env;
14use std::sync::Arc;
15use tokio::sync::broadcast::{channel, Sender};
16use tracing::{info, level_filters::LevelFilter};
17use tracing_subscriber::fmt::time::UtcTime;
18use tracing_subscriber::{fmt, prelude::*, EnvFilter};
17 19
18mod auth;
19mod config; 20mod config;
20mod routes;
21mod wol;
22mod db; 21mod db;
23mod error; 22mod error;
23mod extractors;
24mod routes;
24mod services; 25mod services;
26mod wol;
25 27
26#[tokio::main] 28#[tokio::main]
27async fn main() -> color_eyre::eyre::Result<()> { 29async fn main() -> color_eyre::eyre::Result<()> {
28
29 color_eyre::install()?; 30 color_eyre::install()?;
30 31
31 unsafe { local_offset::set_soundness(local_offset::Soundness::Unsound); }
32 let time_format = 32 let time_format =
33 time::macros::format_description!("[year]-[month]-[day] [hour]:[minute]:[second]"); 33 time::macros::format_description!("[year]-[month]-[day] [hour]:[minute]:[second]");
34 let loc = LocalTime::new(time_format); 34 let loc = UtcTime::new(time_format);
35
36 let file_appender = tracing_appender::rolling::daily("logs", "webol.log");
37 let (non_blocking, _guard) = tracing_appender::non_blocking(file_appender);
35 38
36 tracing_subscriber::registry() 39 tracing_subscriber::registry()
37 .with(fmt::layer() 40 .with(fmt::layer().with_writer(non_blocking).with_ansi(false))
38 .with_timer(loc) 41 .with(fmt::layer().with_timer(loc))
39 )
40 .with( 42 .with(
41 EnvFilter::builder() 43 EnvFilter::builder()
42 .with_default_directive(LevelFilter::INFO.into()) 44 .with_default_directive(LevelFilter::INFO.into())
@@ -56,8 +58,13 @@ async fn main() -> color_eyre::eyre::Result<()> {
56 let (tx, _) = channel(32); 58 let (tx, _) = channel(32);
57 59
58 let ping_map: StatusMap = DashMap::new(); 60 let ping_map: StatusMap = DashMap::new();
59 61
60 let shared_state = Arc::new(AppState { db, config: config.clone(), ping_send: tx, ping_map }); 62 let shared_state = AppState {
63 db,
64 config: config.clone(),
65 ping_send: tx,
66 ping_map,
67 };
61 68
62 let app = Router::new() 69 let app = Router::new()
63 .route("/start", post(start)) 70 .route("/start", post(start))
@@ -65,20 +72,21 @@ async fn main() -> color_eyre::eyre::Result<()> {
65 .route("/device", put(device::put)) 72 .route("/device", put(device::put))
66 .route("/device", post(device::post)) 73 .route("/device", post(device::post))
67 .route("/status", get(status)) 74 .route("/status", get(status))
68 .with_state(shared_state); 75 .route_layer(from_fn_with_state(shared_state.clone(), extractors::auth))
76 .with_state(Arc::new(shared_state));
69 77
70 let addr = config.serveraddr; 78 let addr = config.serveraddr;
71 info!("start server on {}", addr); 79 info!("start server on {}", addr);
72 let listener = tokio::net::TcpListener::bind(addr) 80 let listener = tokio::net::TcpListener::bind(addr).await?;
73 .await?;
74 axum::serve(listener, app).await?; 81 axum::serve(listener, app).await?;
75 82
76 Ok(()) 83 Ok(())
77} 84}
78 85
86#[derive(Clone)]
79pub struct AppState { 87pub struct AppState {
80 db: PgPool, 88 db: PgPool,
81 config: Config, 89 config: Config,
82 ping_send: Sender<BroadcastCommands>, 90 ping_send: Sender<BroadcastCommand>,
83 ping_map: StatusMap, 91 ping_map: StatusMap,
84} 92}
diff --git a/src/routes/mod.rs b/src/routes.rs
index d5ab0d6..d5ab0d6 100644
--- a/src/routes/mod.rs
+++ b/src/routes.rs
diff --git a/src/routes/device.rs b/src/routes/device.rs
index c85df1b..d39d98e 100644
--- a/src/routes/device.rs
+++ b/src/routes/device.rs
@@ -1,34 +1,34 @@
1use std::sync::Arc; 1use crate::db::Device;
2use crate::error::Error;
2use axum::extract::State; 3use axum::extract::State;
3use axum::Json; 4use axum::Json;
4use axum::http::HeaderMap; 5use mac_address::MacAddress;
5use serde::{Deserialize, Serialize}; 6use serde::{Deserialize, Serialize};
6use serde_json::{json, Value}; 7use serde_json::{json, Value};
8use sqlx::types::ipnetwork::IpNetwork;
9use std::{sync::Arc, str::FromStr};
7use tracing::{debug, info}; 10use tracing::{debug, info};
8use crate::auth::auth;
9use crate::db::Device;
10use crate::error::Error;
11 11
12pub async fn get(State(state): State<Arc<crate::AppState>>, headers: HeaderMap, Json(payload): Json<GetDevicePayload>) -> Result<Json<Value>, Error> { 12pub async fn get(
13 info!("add device {}", payload.id); 13 State(state): State<Arc<crate::AppState>>,
14 let secret = headers.get("authorization"); 14 Json(payload): Json<GetDevicePayload>,
15 if auth(&state.config, secret).map_err(Error::Auth)? { 15) -> Result<Json<Value>, Error> {
16 let device = sqlx::query_as!( 16 info!("get device {}", payload.id);
17 Device, 17 let device = sqlx::query_as!(
18 r#" 18 Device,
19 SELECT id, mac, broadcast_addr, ip, times 19 r#"
20 FROM devices 20 SELECT id, mac, broadcast_addr, ip, times
21 WHERE id = $1; 21 FROM devices
22 "#, 22 WHERE id = $1;
23 payload.id 23 "#,
24 ).fetch_one(&state.db).await.map_err(Error::DB)?; 24 payload.id
25 )
26 .fetch_one(&state.db)
27 .await?;
25 28
26 debug!("got device {:?}", device); 29 debug!("got device {:?}", device);
27 30
28 Ok(Json(json!(device))) 31 Ok(Json(json!(device)))
29 } else {
30 Err(Error::Generic)
31 }
32} 32}
33 33
34#[derive(Deserialize)] 34#[derive(Deserialize)]
@@ -36,25 +36,31 @@ pub struct GetDevicePayload {
36 id: String, 36 id: String,
37} 37}
38 38
39pub async fn put(State(state): State<Arc<crate::AppState>>, headers: HeaderMap, Json(payload): Json<PutDevicePayload>) -> Result<Json<Value>, Error> { 39pub async fn put(
40 info!("add device {} ({}, {}, {})", payload.id, payload.mac, payload.broadcast_addr, payload.ip); 40 State(state): State<Arc<crate::AppState>>,
41 let secret = headers.get("authorization"); 41 Json(payload): Json<PutDevicePayload>,
42 if auth(&state.config, secret).map_err(Error::Auth)? { 42) -> Result<Json<Value>, Error> {
43 sqlx::query!( 43 info!(
44 r#" 44 "add device {} ({}, {}, {})",
45 INSERT INTO devices (id, mac, broadcast_addr, ip) 45 payload.id, payload.mac, payload.broadcast_addr, payload.ip
46 VALUES ($1, $2, $3, $4); 46 );
47 "#, 47
48 payload.id, 48 let ip = IpNetwork::from_str(&payload.ip)?;
49 payload.mac, 49 let mac = MacAddress::from_str(&payload.mac)?;
50 payload.broadcast_addr, 50 sqlx::query!(
51 payload.ip 51 r#"
52 ).execute(&state.db).await.map_err(Error::DB)?; 52 INSERT INTO devices (id, mac, broadcast_addr, ip)
53 VALUES ($1, $2, $3, $4);
54 "#,
55 payload.id,
56 mac,
57 payload.broadcast_addr,
58 ip
59 )
60 .execute(&state.db)
61 .await?;
53 62
54 Ok(Json(json!(PutDeviceResponse { success: true }))) 63 Ok(Json(json!(PutDeviceResponse { success: true })))
55 } else {
56 Err(Error::Generic)
57 }
58} 64}
59 65
60#[derive(Deserialize)] 66#[derive(Deserialize)]
@@ -62,35 +68,40 @@ pub struct PutDevicePayload {
62 id: String, 68 id: String,
63 mac: String, 69 mac: String,
64 broadcast_addr: String, 70 broadcast_addr: String,
65 ip: String 71 ip: String,
66} 72}
67 73
68#[derive(Serialize)] 74#[derive(Serialize)]
69pub struct PutDeviceResponse { 75pub struct PutDeviceResponse {
70 success: bool 76 success: bool,
71} 77}
72 78
73pub async fn post(State(state): State<Arc<crate::AppState>>, headers: HeaderMap, Json(payload): Json<PostDevicePayload>) -> Result<Json<Value>, Error> { 79pub async fn post(
74 info!("edit device {} ({}, {}, {})", payload.id, payload.mac, payload.broadcast_addr, payload.ip); 80 State(state): State<Arc<crate::AppState>>,
75 let secret = headers.get("authorization"); 81 Json(payload): Json<PostDevicePayload>,
76 if auth(&state.config, secret).map_err(Error::Auth)? { 82) -> Result<Json<Value>, Error> {
77 let device = sqlx::query_as!( 83 info!(
78 Device, 84 "edit device {} ({}, {}, {})",
79 r#" 85 payload.id, payload.mac, payload.broadcast_addr, payload.ip
80 UPDATE devices 86 );
81 SET mac = $1, broadcast_addr = $2, ip = $3 WHERE id = $4 87 let ip = IpNetwork::from_str(&payload.ip)?;
82 RETURNING id, mac, broadcast_addr, ip, times; 88 let mac = MacAddress::from_str(&payload.mac)?;
83 "#, 89 let device = sqlx::query_as!(
84 payload.mac, 90 Device,
85 payload.broadcast_addr, 91 r#"
86 payload.ip, 92 UPDATE devices
87 payload.id 93 SET mac = $1, broadcast_addr = $2, ip = $3 WHERE id = $4
88 ).fetch_one(&state.db).await.map_err(Error::DB)?; 94 RETURNING id, mac, broadcast_addr, ip, times;
95 "#,
96 mac,
97 payload.broadcast_addr,
98 ip,
99 payload.id
100 )
101 .fetch_one(&state.db)
102 .await?;
89 103
90 Ok(Json(json!(device))) 104 Ok(Json(json!(device)))
91 } else {
92 Err(Error::Generic)
93 }
94} 105}
95 106
96#[derive(Deserialize)] 107#[derive(Deserialize)]
diff --git a/src/routes/start.rs b/src/routes/start.rs
index ce95bf3..d4c0802 100644
--- a/src/routes/start.rs
+++ b/src/routes/start.rs
@@ -1,10 +1,8 @@
1use crate::auth::auth;
2use crate::db::Device; 1use crate::db::Device;
3use crate::error::Error; 2use crate::error::Error;
4use crate::services::ping::Value as PingValue; 3use crate::services::ping::Value as PingValue;
5use crate::wol::{create_buffer, send_packet}; 4use crate::wol::{create_buffer, send_packet};
6use axum::extract::State; 5use axum::extract::State;
7use axum::http::HeaderMap;
8use axum::Json; 6use axum::Json;
9use serde::{Deserialize, Serialize}; 7use serde::{Deserialize, Serialize};
10use serde_json::{json, Value}; 8use serde_json::{json, Value};
@@ -12,86 +10,82 @@ use std::sync::Arc;
12use tracing::{debug, info}; 10use tracing::{debug, info};
13use uuid::Uuid; 11use uuid::Uuid;
14 12
15#[axum_macros::debug_handler]
16pub async fn start( 13pub async fn start(
17 State(state): State<Arc<crate::AppState>>, 14 State(state): State<Arc<crate::AppState>>,
18 headers: HeaderMap,
19 Json(payload): Json<Payload>, 15 Json(payload): Json<Payload>,
20) -> Result<Json<Value>, Error> { 16) -> Result<Json<Value>, Error> {
21 info!("POST request"); 17 info!("POST request");
22 let secret = headers.get("authorization"); 18 let device = sqlx::query_as!(
23 let authorized = auth(&state.config, secret).map_err(Error::Auth)?; 19 Device,
24 if authorized { 20 r#"
25 let device = sqlx::query_as!( 21 SELECT id, mac, broadcast_addr, ip, times
26 Device, 22 FROM devices
27 r#" 23 WHERE id = $1;
28 SELECT id, mac, broadcast_addr, ip, times 24 "#,
29 FROM devices 25 payload.id
30 WHERE id = $1; 26 )
31 "#, 27 .fetch_one(&state.db)
32 payload.id 28 .await?;
33 )
34 .fetch_one(&state.db)
35 .await
36 .map_err(Error::DB)?;
37
38 info!("starting {}", device.id);
39
40 let bind_addr = "0.0.0.0:0";
41 29
42 let _ = send_packet( 30 info!("starting {}", device.id);
43 &bind_addr.parse().map_err(Error::IpParse)?,
44 &device.broadcast_addr.parse().map_err(Error::IpParse)?,
45 &create_buffer(&device.mac)?,
46 )?;
47 let dev_id = device.id.clone();
48 let uuid = if payload.ping.is_some_and(|ping| ping) {
49 let mut uuid: Option<String> = None;
50 for (key, value) in state.ping_map.clone() {
51 if value.ip == device.ip {
52 debug!("service already exists");
53 uuid = Some(key);
54 break;
55 }
56 }
57 let uuid_gen = match uuid {
58 Some(u) => u,
59 None => Uuid::new_v4().to_string(),
60 };
61 let uuid_genc = uuid_gen.clone();
62 31
63 tokio::spawn(async move { 32 let bind_addr = "0.0.0.0:0";
64 debug!("init ping service");
65 state.ping_map.insert(
66 uuid_gen.clone(),
67 PingValue {
68 ip: device.ip.clone(),
69 online: false,
70 },
71 );
72 33
73 crate::services::ping::spawn( 34 let _ = send_packet(
74 state.ping_send.clone(), 35 bind_addr,
75 &state.config, 36 &device.broadcast_addr,
76 device, 37 &create_buffer(&device.mac.to_string())?,
77 uuid_gen.clone(), 38 )?;
78 &state.ping_map, 39 let dev_id = device.id.clone();
79 &state.db, 40 let uuid = if payload.ping.is_some_and(|ping| ping) {
80 ) 41 Some(setup_ping(state, device))
81 .await;
82 });
83 Some(uuid_genc)
84 } else {
85 None
86 };
87 Ok(Json(json!(Response {
88 id: dev_id,
89 boot: true,
90 uuid
91 })))
92 } else { 42 } else {
93 Err(Error::Generic) 43 None
44 };
45 Ok(Json(json!(Response {
46 id: dev_id,
47 boot: true,
48 uuid
49 })))
50}
51
52fn setup_ping(state: Arc<crate::AppState>, device: Device) -> String {
53 let mut uuid: Option<String> = None;
54 for (key, value) in state.ping_map.clone() {
55 if value.ip == device.ip {
56 debug!("service already exists");
57 uuid = Some(key);
58 break;
59 }
94 } 60 }
61 let uuid_gen = match uuid {
62 Some(u) => u,
63 None => Uuid::new_v4().to_string(),
64 };
65 let uuid_ret = uuid_gen.clone();
66
67 debug!("init ping service");
68 state.ping_map.insert(
69 uuid_gen.clone(),
70 PingValue {
71 ip: device.ip,
72 online: false,
73 },
74 );
75
76 tokio::spawn(async move {
77 crate::services::ping::spawn(
78 state.ping_send.clone(),
79 &state.config,
80 device,
81 uuid_gen,
82 &state.ping_map,
83 &state.db,
84 )
85 .await;
86 });
87
88 uuid_ret
95} 89}
96 90
97#[derive(Deserialize)] 91#[derive(Deserialize)]
diff --git a/src/routes/status.rs b/src/routes/status.rs
index 31ef996..0e25f7d 100644
--- a/src/routes/status.rs
+++ b/src/routes/status.rs
@@ -1,10 +1,79 @@
1use std::sync::Arc; 1use crate::services::ping::BroadcastCommand;
2use crate::AppState;
3use axum::extract::ws::{Message, WebSocket};
2use axum::extract::{State, WebSocketUpgrade}; 4use axum::extract::{State, WebSocketUpgrade};
3use axum::response::Response; 5use axum::response::Response;
4use crate::AppState; 6use sqlx::PgPool;
5use crate::services::ping::status_websocket; 7use std::sync::Arc;
8use tracing::{debug, trace};
6 9
7#[axum_macros::debug_handler]
8pub async fn status(State(state): State<Arc<AppState>>, ws: WebSocketUpgrade) -> Response { 10pub async fn status(State(state): State<Arc<AppState>>, ws: WebSocketUpgrade) -> Response {
9 ws.on_upgrade(move |socket| status_websocket(socket, state)) 11 ws.on_upgrade(move |socket| websocket(socket, state))
12}
13
14pub async fn websocket(mut socket: WebSocket, state: Arc<AppState>) {
15 trace!("wait for ws message (uuid)");
16 let msg = socket.recv().await;
17 let uuid = msg.unwrap().unwrap().into_text().unwrap();
18
19 trace!("Search for uuid: {}", uuid);
20
21 let eta = get_eta(&state.db).await;
22 let _ = socket
23 .send(Message::Text(format!("eta_{eta}_{uuid}")))
24 .await;
25
26 let device_exists = state.ping_map.contains_key(&uuid);
27 if device_exists {
28 let _ = socket
29 .send(receive_ping_broadcast(state.clone(), uuid).await)
30 .await;
31 } else {
32 debug!("didn't find any device");
33 let _ = socket.send(Message::Text(format!("notfound_{uuid}"))).await;
34 };
35
36 let _ = socket.close().await;
37}
38
39async fn receive_ping_broadcast(state: Arc<AppState>, uuid: String) -> Message {
40 let pm = state.ping_map.clone().into_read_only();
41 let device = pm.get(&uuid).expect("fatal error");
42 debug!("got device: {} (online: {})", device.ip, device.online);
43 if device.online {
44 debug!("already started");
45 Message::Text(BroadcastCommand::success(uuid).to_string())
46 } else {
47 loop {
48 trace!("wait for tx message");
49 let message = state
50 .ping_send
51 .subscribe()
52 .recv()
53 .await
54 .expect("fatal error");
55 trace!("got message {:?}", message);
56
57 if message.uuid != uuid {
58 continue;
59 }
60 trace!("message == uuid success");
61 return Message::Text(message.to_string());
62 }
63 }
64}
65
66async fn get_eta(db: &PgPool) -> i64 {
67 let query = sqlx::query!(r#"SELECT times FROM devices;"#)
68 .fetch_one(db)
69 .await
70 .unwrap();
71
72 let times = if let Some(times) = query.times {
73 times
74 } else {
75 vec![0]
76 };
77
78 times.iter().sum::<i64>() / i64::try_from(times.len()).unwrap()
10} 79}
diff --git a/src/services/mod.rs b/src/services.rs
index a766209..a766209 100644
--- a/src/services/mod.rs
+++ b/src/services.rs
diff --git a/src/services/ping.rs b/src/services/ping.rs
index 9b164c8..9191f86 100644
--- a/src/services/ping.rs
+++ b/src/services/ping.rs
@@ -1,59 +1,58 @@
1use std::str::FromStr; 1use crate::config::Config;
2use std::net::IpAddr; 2use crate::db::Device;
3use std::sync::Arc;
4
5use axum::extract::ws::WebSocket;
6use axum::extract::ws::Message;
7use dashmap::DashMap; 3use dashmap::DashMap;
4use ipnetwork::IpNetwork;
8use sqlx::PgPool; 5use sqlx::PgPool;
6use std::fmt::Display;
9use time::{Duration, Instant}; 7use time::{Duration, Instant};
10use tokio::sync::broadcast::Sender; 8use tokio::sync::broadcast::Sender;
11use tracing::{debug, error, trace}; 9use tracing::{debug, error, trace};
12use crate::AppState;
13use crate::config::Config;
14use crate::db::Device;
15 10
16pub type StatusMap = DashMap<String, Value>; 11pub type StatusMap = DashMap<String, Value>;
17 12
18#[derive(Debug, Clone)] 13#[derive(Debug, Clone)]
19pub struct Value { 14pub struct Value {
20 pub ip: String, 15 pub ip: IpNetwork,
21 pub online: bool 16 pub online: bool,
22} 17}
23 18
24pub async fn spawn(tx: Sender<BroadcastCommands>, config: &Config, device: Device, uuid: String, ping_map: &StatusMap, db: &PgPool) { 19pub async fn spawn(
20 tx: Sender<BroadcastCommand>,
21 config: &Config,
22 device: Device,
23 uuid: String,
24 ping_map: &StatusMap,
25 db: &PgPool,
26) {
25 let timer = Instant::now(); 27 let timer = Instant::now();
26 let payload = [0; 8]; 28 let payload = [0; 8];
27 29
28 let ping_ip = IpAddr::from_str(&device.ip).expect("bad ip"); 30 let mut msg: Option<BroadcastCommand> = None;
29
30 let mut msg: Option<BroadcastCommands> = None;
31 while msg.is_none() { 31 while msg.is_none() {
32 let ping = surge_ping::ping( 32 let ping = surge_ping::ping(device.ip.ip(), &payload).await;
33 ping_ip,
34 &payload
35 ).await;
36 33
37 if let Err(ping) = ping { 34 if let Err(ping) = ping {
38 let ping_timeout = matches!(ping, surge_ping::SurgeError::Timeout { .. }); 35 let ping_timeout = matches!(ping, surge_ping::SurgeError::Timeout { .. });
39 if !ping_timeout { 36 if !ping_timeout {
40 error!("{}", ping.to_string()); 37 error!("{}", ping.to_string());
41 msg = Some(BroadcastCommands::Error(uuid.clone())); 38 msg = Some(BroadcastCommand::error(uuid.clone()));
42 } 39 }
43 if timer.elapsed() >= Duration::minutes(config.pingtimeout) { 40 if timer.elapsed() >= Duration::minutes(config.pingtimeout) {
44 msg = Some(BroadcastCommands::Timeout(uuid.clone())); 41 msg = Some(BroadcastCommand::timeout(uuid.clone()));
45 } 42 }
46 } else { 43 } else {
47 let (_, duration) = ping.map_err(|err| error!("{}", err.to_string())).expect("fatal error"); 44 let (_, duration) = ping
45 .map_err(|err| error!("{}", err.to_string()))
46 .expect("fatal error");
48 debug!("ping took {:?}", duration); 47 debug!("ping took {:?}", duration);
49 msg = Some(BroadcastCommands::Success(uuid.clone())); 48 msg = Some(BroadcastCommand::success(uuid.clone()));
50 }; 49 };
51 } 50 }
52 51
53 let msg = msg.expect("fatal error"); 52 let msg = msg.expect("fatal error");
54 53
55 let _ = tx.send(msg.clone()); 54 let _ = tx.send(msg.clone());
56 if let BroadcastCommands::Success(..) = msg { 55 if let BroadcastCommands::Success = msg.command {
57 sqlx::query!( 56 sqlx::query!(
58 r#" 57 r#"
59 UPDATE devices 58 UPDATE devices
@@ -62,8 +61,17 @@ pub async fn spawn(tx: Sender<BroadcastCommands>, config: &Config, device: Devic
62 "#, 61 "#,
63 timer.elapsed().whole_seconds(), 62 timer.elapsed().whole_seconds(),
64 device.id 63 device.id
65 ).execute(db).await.unwrap(); 64 )
66 ping_map.insert(uuid.clone(), Value { ip: device.ip.clone(), online: true }); 65 .execute(db)
66 .await
67 .unwrap();
68 ping_map.insert(
69 uuid.clone(),
70 Value {
71 ip: device.ip,
72 online: true,
73 },
74 );
67 tokio::time::sleep(tokio::time::Duration::from_secs(60)).await; 75 tokio::time::sleep(tokio::time::Duration::from_secs(60)).await;
68 } 76 }
69 trace!("remove {} from ping_map", uuid); 77 trace!("remove {} from ping_map", uuid);
@@ -72,74 +80,48 @@ pub async fn spawn(tx: Sender<BroadcastCommands>, config: &Config, device: Devic
72 80
73#[derive(Clone, Debug, PartialEq)] 81#[derive(Clone, Debug, PartialEq)]
74pub enum BroadcastCommands { 82pub enum BroadcastCommands {
75 Success(String), 83 Success,
76 Timeout(String), 84 Timeout,
77 Error(String), 85 Error,
78} 86}
79 87
80pub async fn status_websocket(mut socket: WebSocket, state: Arc<AppState>) { 88#[derive(Clone, Debug, PartialEq)]
81 trace!("wait for ws message (uuid)"); 89pub struct BroadcastCommand {
82 let msg = socket.recv().await; 90 pub uuid: String,
83 let uuid = msg.unwrap().unwrap().into_text().unwrap(); 91 pub command: BroadcastCommands,
84 92}
85 trace!("Search for uuid: {}", uuid);
86
87 let eta = get_eta(&state.db).await;
88 let _ = socket.send(Message::Text(format!("eta_{eta}_{uuid}"))).await;
89 93
90 let device_exists = state.ping_map.contains_key(&uuid); 94impl Display for BroadcastCommand {
91 if device_exists { 95 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
92 let _ = socket.send(process_device(state.clone(), uuid).await).await; 96 let prefix = match self.command {
93 } else { 97 BroadcastCommands::Success => "start",
94 debug!("didn't find any device"); 98 BroadcastCommands::Timeout => "timeout",
95 let _ = socket.send(Message::Text(format!("notfound_{uuid}"))).await; 99 BroadcastCommands::Error => "error",
96 }; 100 };
97 101
98 let _ = socket.close().await; 102 f.write_str(format!("{prefix}_{}", self.uuid).as_str())
103 }
99} 104}
100 105
101async fn get_eta(db: &PgPool) -> i64 { 106impl BroadcastCommand {
102 let query = sqlx::query!( 107 pub fn success(uuid: String) -> Self {
103 r#"SELECT times FROM devices;"# 108 Self {
104 ).fetch_one(db).await.unwrap(); 109 uuid,
105 110 command: BroadcastCommands::Success,
106 let times = match query.times { 111 }
107 None => { vec![0] }, 112 }
108 Some(t) => t,
109 };
110 times.iter().sum::<i64>() / i64::try_from(times.len()).unwrap()
111 113
112} 114 pub fn timeout(uuid: String) -> Self {
115 Self {
116 uuid,
117 command: BroadcastCommands::Timeout,
118 }
119 }
113 120
114async fn process_device(state: Arc<AppState>, uuid: String) -> Message { 121 pub fn error(uuid: String) -> Self {
115 let pm = state.ping_map.clone().into_read_only(); 122 Self {
116 let device = pm.get(&uuid).expect("fatal error"); 123 uuid,
117 debug!("got device: {} (online: {})", device.ip, device.online); 124 command: BroadcastCommands::Error,
118 if device.online {
119 debug!("already started");
120 Message::Text(format!("start_{uuid}"))
121 } else {
122 loop {
123 trace!("wait for tx message");
124 let message = state.ping_send.subscribe().recv().await.expect("fatal error");
125 trace!("got message {:?}", message);
126 return match message {
127 BroadcastCommands::Success(msg_uuid) => {
128 if msg_uuid != uuid { continue; }
129 trace!("message == uuid success");
130 Message::Text(format!("start_{uuid}"))
131 },
132 BroadcastCommands::Timeout(msg_uuid) => {
133 if msg_uuid != uuid { continue; }
134 trace!("message == uuid timeout");
135 Message::Text(format!("timeout_{uuid}"))
136 },
137 BroadcastCommands::Error(msg_uuid) => {
138 if msg_uuid != uuid { continue; }
139 trace!("message == uuid error");
140 Message::Text(format!("error_{uuid}"))
141 }
142 }
143 } 125 }
144 } 126 }
145} 127}
diff --git a/src/wol.rs b/src/wol.rs
index 83c0ee6..31cf350 100644
--- a/src/wol.rs
+++ b/src/wol.rs
@@ -1,4 +1,4 @@
1use std::net::{SocketAddr, UdpSocket}; 1use std::net::{ToSocketAddrs, UdpSocket};
2 2
3use crate::error::Error; 3use crate::error::Error;
4 4
@@ -11,8 +11,8 @@ pub fn create_buffer(mac_addr: &str) -> Result<Vec<u8>, Error> {
11 let mut mac = Vec::new(); 11 let mut mac = Vec::new();
12 let sp = mac_addr.split(':'); 12 let sp = mac_addr.split(':');
13 for f in sp { 13 for f in sp {
14 mac.push(u8::from_str_radix(f, 16).map_err(Error::BufferParse)?); 14 mac.push(u8::from_str_radix(f, 16)?);
15 }; 15 }
16 let mut buf = vec![255; 6]; 16 let mut buf = vec![255; 6];
17 for _ in 0..16 { 17 for _ in 0..16 {
18 for i in &mac { 18 for i in &mac {
@@ -23,8 +23,12 @@ pub fn create_buffer(mac_addr: &str) -> Result<Vec<u8>, Error> {
23} 23}
24 24
25/// Sends a buffer on UDP broadcast 25/// Sends a buffer on UDP broadcast
26pub fn send_packet(bind_addr: &SocketAddr, broadcast_addr: &SocketAddr, buffer: &[u8]) -> Result<usize, Error> { 26pub fn send_packet<A: ToSocketAddrs>(
27 let socket = UdpSocket::bind(bind_addr).map_err(Error::Broadcast)?; 27 bind_addr: A,
28 socket.set_broadcast(true).map_err(Error::Broadcast)?; 28 broadcast_addr: A,
29 socket.send_to(buffer, broadcast_addr).map_err(Error::Broadcast) 29 buffer: &[u8],
30) -> Result<usize, Error> {
31 let socket = UdpSocket::bind(bind_addr)?;
32 socket.set_broadcast(true)?;
33 Ok(socket.send_to(buffer, broadcast_addr)?)
30} 34}