mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 12:11:56 +00:00
Merge branch 'next' of github.com:Start9Labs/start-os into rebase/integration/refactors
This commit is contained in:
16
backend/.sqlx/query-1ce5254f27de971fd87f5ab66d300f2b22433c86617a0dbf796bf2170186dd2e.json
generated
Normal file
16
backend/.sqlx/query-1ce5254f27de971fd87f5ab66d300f2b22433c86617a0dbf796bf2170186dd2e.json
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO network_keys (package, interface, key) VALUES ($1, $2, $3) ON CONFLICT (package, interface) DO NOTHING",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text",
|
||||
"Bytea"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "1ce5254f27de971fd87f5ab66d300f2b22433c86617a0dbf796bf2170186dd2e"
|
||||
}
|
||||
14
backend/.sqlx/query-21471490cdc3adb206274cc68e1ea745ffa5da4479478c1fd2158a45324b1930.json
generated
Normal file
14
backend/.sqlx/query-21471490cdc3adb206274cc68e1ea745ffa5da4479478c1fd2158a45324b1930.json
generated
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "DELETE FROM ssh_keys WHERE fingerprint = $1",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "21471490cdc3adb206274cc68e1ea745ffa5da4479478c1fd2158a45324b1930"
|
||||
}
|
||||
40
backend/.sqlx/query-28ea34bbde836e0618c5fc9bb7c36e463c20c841a7d6a0eb15be0f24f4a928ec.json
generated
Normal file
40
backend/.sqlx/query-28ea34bbde836e0618c5fc9bb7c36e463c20c841a7d6a0eb15be0f24f4a928ec.json
generated
Normal file
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT hostname, path, username, password FROM cifs_shares WHERE id = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "hostname",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "path",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "username",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "password",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int4"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "28ea34bbde836e0618c5fc9bb7c36e463c20c841a7d6a0eb15be0f24f4a928ec"
|
||||
}
|
||||
34
backend/.sqlx/query-4099028a5c0de578255bf54a67cef6cb0f1e9a4e158260700f1639dd4b438997.json
generated
Normal file
34
backend/.sqlx/query-4099028a5c0de578255bf54a67cef6cb0f1e9a4e158260700f1639dd4b438997.json
generated
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT * FROM ssh_keys WHERE fingerprint = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "fingerprint",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "openssh_pubkey",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "created_at",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "4099028a5c0de578255bf54a67cef6cb0f1e9a4e158260700f1639dd4b438997"
|
||||
}
|
||||
50
backend/.sqlx/query-4691e3a2ce80b59009ac17124f54f925f61dc5ea371903e62cdffa5d7b67ca96.json
generated
Normal file
50
backend/.sqlx/query-4691e3a2ce80b59009ac17124f54f925f61dc5ea371903e62cdffa5d7b67ca96.json
generated
Normal file
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT * FROM session WHERE logged_out IS NULL OR logged_out > CURRENT_TIMESTAMP",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "logged_in",
|
||||
"type_info": "Timestamp"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "logged_out",
|
||||
"type_info": "Timestamp"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "last_active",
|
||||
"type_info": "Timestamp"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "user_agent",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "metadata",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "4691e3a2ce80b59009ac17124f54f925f61dc5ea371903e62cdffa5d7b67ca96"
|
||||
}
|
||||
14
backend/.sqlx/query-4bcfbefb1eb3181343871a1cd7fc3afb81c2be5c681cfa8b4be0ce70610e9c3a.json
generated
Normal file
14
backend/.sqlx/query-4bcfbefb1eb3181343871a1cd7fc3afb81c2be5c681cfa8b4be0ce70610e9c3a.json
generated
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE session SET logged_out = CURRENT_TIMESTAMP WHERE id = $1",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "4bcfbefb1eb3181343871a1cd7fc3afb81c2be5c681cfa8b4be0ce70610e9c3a"
|
||||
}
|
||||
20
backend/.sqlx/query-629be61c3c341c131ddbbff0293a83dbc6afd07cae69d246987f62cf0cc35c2a.json
generated
Normal file
20
backend/.sqlx/query-629be61c3c341c131ddbbff0293a83dbc6afd07cae69d246987f62cf0cc35c2a.json
generated
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT password FROM account",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "password",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "629be61c3c341c131ddbbff0293a83dbc6afd07cae69d246987f62cf0cc35c2a"
|
||||
}
|
||||
23
backend/.sqlx/query-687688055e63d27123cdc89a5bbbd8361776290a9411d527eaf1fdb40bef399d.json
generated
Normal file
23
backend/.sqlx/query-687688055e63d27123cdc89a5bbbd8361776290a9411d527eaf1fdb40bef399d.json
generated
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT key FROM tor WHERE package = $1 AND interface = $2",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "key",
|
||||
"type_info": "Bytea"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "687688055e63d27123cdc89a5bbbd8361776290a9411d527eaf1fdb40bef399d"
|
||||
}
|
||||
14
backend/.sqlx/query-6d35ccf780fb2bb62586dd1d3df9c1550a41ee580dad3f49d35cb843ebef10ca.json
generated
Normal file
14
backend/.sqlx/query-6d35ccf780fb2bb62586dd1d3df9c1550a41ee580dad3f49d35cb843ebef10ca.json
generated
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE session SET last_active = CURRENT_TIMESTAMP WHERE id = $1 AND logged_out IS NULL OR logged_out > CURRENT_TIMESTAMP",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "6d35ccf780fb2bb62586dd1d3df9c1550a41ee580dad3f49d35cb843ebef10ca"
|
||||
}
|
||||
24
backend/.sqlx/query-770c1017734720453dc87b58c385b987c5af5807151ff71a59000014586752e0.json
generated
Normal file
24
backend/.sqlx/query-770c1017734720453dc87b58c385b987c5af5807151ff71a59000014586752e0.json
generated
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO network_keys (package, interface, key) VALUES ($1, $2, $3) ON CONFLICT (package, interface) DO UPDATE SET package = EXCLUDED.package RETURNING key",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "key",
|
||||
"type_info": "Bytea"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text",
|
||||
"Bytea"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "770c1017734720453dc87b58c385b987c5af5807151ff71a59000014586752e0"
|
||||
}
|
||||
65
backend/.sqlx/query-7b64f032d507e8ffe37c41f4c7ad514a66c421a11ab04c26d89a7aa8f6b67210.json
generated
Normal file
65
backend/.sqlx/query-7b64f032d507e8ffe37c41f4c7ad514a66c421a11ab04c26d89a7aa8f6b67210.json
generated
Normal file
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT id, package_id, created_at, code, level, title, message, data FROM notifications WHERE id < $1 ORDER BY id DESC LIMIT $2",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "package_id",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "created_at",
|
||||
"type_info": "Timestamp"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "code",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "level",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "title",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "message",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "data",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int4",
|
||||
"Int8"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "7b64f032d507e8ffe37c41f4c7ad514a66c421a11ab04c26d89a7aa8f6b67210"
|
||||
}
|
||||
19
backend/.sqlx/query-7c7a3549c997eb75bf964ea65fbb98a73045adf618696cd838d79203ef5383fb.json
generated
Normal file
19
backend/.sqlx/query-7c7a3549c997eb75bf964ea65fbb98a73045adf618696cd838d79203ef5383fb.json
generated
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n INSERT INTO account (\n id,\n server_id,\n hostname,\n password,\n network_key,\n root_ca_key_pem,\n root_ca_cert_pem\n ) VALUES (\n 0, $1, $2, $3, $4, $5, $6\n ) ON CONFLICT (id) DO UPDATE SET\n server_id = EXCLUDED.server_id,\n hostname = EXCLUDED.hostname,\n password = EXCLUDED.password,\n network_key = EXCLUDED.network_key,\n root_ca_key_pem = EXCLUDED.root_ca_key_pem,\n root_ca_cert_pem = EXCLUDED.root_ca_cert_pem\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text",
|
||||
"Text",
|
||||
"Bytea",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "7c7a3549c997eb75bf964ea65fbb98a73045adf618696cd838d79203ef5383fb"
|
||||
}
|
||||
14
backend/.sqlx/query-7e0649d839927e57fa03ee51a2c9f96a8bdb0fc97ee8a3c6df1069e1e2b98576.json
generated
Normal file
14
backend/.sqlx/query-7e0649d839927e57fa03ee51a2c9f96a8bdb0fc97ee8a3c6df1069e1e2b98576.json
generated
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "DELETE FROM tor WHERE package = $1",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "7e0649d839927e57fa03ee51a2c9f96a8bdb0fc97ee8a3c6df1069e1e2b98576"
|
||||
}
|
||||
16
backend/.sqlx/query-8951b9126fbf60dbb5997241e11e3526b70bccf3e407327917294a993bc17ed5.json
generated
Normal file
16
backend/.sqlx/query-8951b9126fbf60dbb5997241e11e3526b70bccf3e407327917294a993bc17ed5.json
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO tor (package, interface, key) VALUES ($1, $2, $3) ON CONFLICT (package, interface) DO NOTHING",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text",
|
||||
"Bytea"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "8951b9126fbf60dbb5997241e11e3526b70bccf3e407327917294a993bc17ed5"
|
||||
}
|
||||
64
backend/.sqlx/query-94d471bb374b4965c6cbedf8c17bbf6bea226d38efaf6559923c79a36d5ca08c.json
generated
Normal file
64
backend/.sqlx/query-94d471bb374b4965c6cbedf8c17bbf6bea226d38efaf6559923c79a36d5ca08c.json
generated
Normal file
@@ -0,0 +1,64 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT id, package_id, created_at, code, level, title, message, data FROM notifications ORDER BY id DESC LIMIT $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "package_id",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "created_at",
|
||||
"type_info": "Timestamp"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "code",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "level",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "title",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "message",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "data",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "94d471bb374b4965c6cbedf8c17bbf6bea226d38efaf6559923c79a36d5ca08c"
|
||||
}
|
||||
44
backend/.sqlx/query-95c4ab4c645f3302568c6ff13d85ab58252362694cf0f56999bf60194d20583a.json
generated
Normal file
44
backend/.sqlx/query-95c4ab4c645f3302568c6ff13d85ab58252362694cf0f56999bf60194d20583a.json
generated
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT id, hostname, path, username, password FROM cifs_shares",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "hostname",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "path",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "username",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "password",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "95c4ab4c645f3302568c6ff13d85ab58252362694cf0f56999bf60194d20583a"
|
||||
}
|
||||
14
backend/.sqlx/query-a60d6e66719325b08dc4ecfacaf337527233c84eee758ac9be967906e5841d27.json
generated
Normal file
14
backend/.sqlx/query-a60d6e66719325b08dc4ecfacaf337527233c84eee758ac9be967906e5841d27.json
generated
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "DELETE FROM cifs_shares WHERE id = $1",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int4"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "a60d6e66719325b08dc4ecfacaf337527233c84eee758ac9be967906e5841d27"
|
||||
}
|
||||
32
backend/.sqlx/query-a6b0c8909a3a5d6d9156aebfb359424e6b5a1d1402e028219e21726f1ebd282e.json
generated
Normal file
32
backend/.sqlx/query-a6b0c8909a3a5d6d9156aebfb359424e6b5a1d1402e028219e21726f1ebd282e.json
generated
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT fingerprint, openssh_pubkey, created_at FROM ssh_keys",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "fingerprint",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "openssh_pubkey",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "created_at",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "a6b0c8909a3a5d6d9156aebfb359424e6b5a1d1402e028219e21726f1ebd282e"
|
||||
}
|
||||
18
backend/.sqlx/query-b1147beaaabbed89f2ab8c1e13ec4393a9a8fde2833cf096af766a979d94dee6.json
generated
Normal file
18
backend/.sqlx/query-b1147beaaabbed89f2ab8c1e13ec4393a9a8fde2833cf096af766a979d94dee6.json
generated
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE cifs_shares SET hostname = $1, path = $2, username = $3, password = $4 WHERE id = $5",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text",
|
||||
"Text",
|
||||
"Text",
|
||||
"Int4"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "b1147beaaabbed89f2ab8c1e13ec4393a9a8fde2833cf096af766a979d94dee6"
|
||||
}
|
||||
20
backend/.sqlx/query-d5117054072476377f3c4f040ea429d4c9b2cf534e76f35c80a2bf60e8599cca.json
generated
Normal file
20
backend/.sqlx/query-d5117054072476377f3c4f040ea429d4c9b2cf534e76f35c80a2bf60e8599cca.json
generated
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT openssh_pubkey FROM ssh_keys",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "openssh_pubkey",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "d5117054072476377f3c4f040ea429d4c9b2cf534e76f35c80a2bf60e8599cca"
|
||||
}
|
||||
19
backend/.sqlx/query-da71f94b29798d1738d2b10b9a721ea72db8cfb362e7181c8226d9297507c62b.json
generated
Normal file
19
backend/.sqlx/query-da71f94b29798d1738d2b10b9a721ea72db8cfb362e7181c8226d9297507c62b.json
generated
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO notifications (package_id, code, level, title, message, data) VALUES ($1, $2, $3, $4, $5, $6)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Int4",
|
||||
"Text",
|
||||
"Text",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "da71f94b29798d1738d2b10b9a721ea72db8cfb362e7181c8226d9297507c62b"
|
||||
}
|
||||
14
backend/.sqlx/query-e185203cf84e43b801dfb23b4159e34aeaef1154dcd3d6811ab504915497ccf7.json
generated
Normal file
14
backend/.sqlx/query-e185203cf84e43b801dfb23b4159e34aeaef1154dcd3d6811ab504915497ccf7.json
generated
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "DELETE FROM notifications WHERE id = $1",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int4"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "e185203cf84e43b801dfb23b4159e34aeaef1154dcd3d6811ab504915497ccf7"
|
||||
}
|
||||
20
backend/.sqlx/query-e545696735f202f9d13cf22a561f3ff3f9aed7f90027a9ba97634bcb47d772f0.json
generated
Normal file
20
backend/.sqlx/query-e545696735f202f9d13cf22a561f3ff3f9aed7f90027a9ba97634bcb47d772f0.json
generated
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT tor_key FROM account WHERE id = 0",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "tor_key",
|
||||
"type_info": "Bytea"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "e545696735f202f9d13cf22a561f3ff3f9aed7f90027a9ba97634bcb47d772f0"
|
||||
}
|
||||
16
backend/.sqlx/query-e5843c5b0e7819b29aa1abf2266799bd4f82e761837b526a0972c3d4439a264d.json
generated
Normal file
16
backend/.sqlx/query-e5843c5b0e7819b29aa1abf2266799bd4f82e761837b526a0972c3d4439a264d.json
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO session (id, user_agent, metadata) VALUES ($1, $2, $3)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "e5843c5b0e7819b29aa1abf2266799bd4f82e761837b526a0972c3d4439a264d"
|
||||
}
|
||||
40
backend/.sqlx/query-e95322a8e2ae3b93f1e974b24c0b81803f1e9ec9e8ebbf15cafddfc1c5a028ed.json
generated
Normal file
40
backend/.sqlx/query-e95322a8e2ae3b93f1e974b24c0b81803f1e9ec9e8ebbf15cafddfc1c5a028ed.json
generated
Normal file
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT\n network_keys.package,\n network_keys.interface,\n network_keys.key,\n tor.key AS \"tor_key?\"\n FROM\n network_keys\n LEFT JOIN\n tor\n ON\n network_keys.package = tor.package\n AND\n network_keys.interface = tor.interface\n WHERE\n network_keys.package = $1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "package",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "interface",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "key",
|
||||
"type_info": "Bytea"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "tor_key?",
|
||||
"type_info": "Bytea"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "e95322a8e2ae3b93f1e974b24c0b81803f1e9ec9e8ebbf15cafddfc1c5a028ed"
|
||||
}
|
||||
14
backend/.sqlx/query-eb750adaa305bdbf3c5b70aaf59139c7b7569602adb58f2d6b3a94da4f167b0a.json
generated
Normal file
14
backend/.sqlx/query-eb750adaa305bdbf3c5b70aaf59139c7b7569602adb58f2d6b3a94da4f167b0a.json
generated
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "DELETE FROM notifications WHERE id < $1",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int4"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "eb750adaa305bdbf3c5b70aaf59139c7b7569602adb58f2d6b3a94da4f167b0a"
|
||||
}
|
||||
25
backend/.sqlx/query-ecc765d8205c0876956f95f76944ac6a5f34dd820c4073b7728c7067aab9fded.json
generated
Normal file
25
backend/.sqlx/query-ecc765d8205c0876956f95f76944ac6a5f34dd820c4073b7728c7067aab9fded.json
generated
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO cifs_shares (hostname, path, username, password) VALUES ($1, $2, $3, $4) RETURNING id",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Int4"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "ecc765d8205c0876956f95f76944ac6a5f34dd820c4073b7728c7067aab9fded"
|
||||
}
|
||||
16
backend/.sqlx/query-f6d1c5ef0f9d9577bea8382318967b9deb46da75788c7fe6082b43821c22d556.json
generated
Normal file
16
backend/.sqlx/query-f6d1c5ef0f9d9577bea8382318967b9deb46da75788c7fe6082b43821c22d556.json
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO ssh_keys (fingerprint, openssh_pubkey, created_at) VALUES ($1, $2, $3)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "f6d1c5ef0f9d9577bea8382318967b9deb46da75788c7fe6082b43821c22d556"
|
||||
}
|
||||
20
backend/.sqlx/query-f7d2dae84613bcef330f7403352cc96547f3f6dbec11bf2eadfaf53ad8ab51b5.json
generated
Normal file
20
backend/.sqlx/query-f7d2dae84613bcef330f7403352cc96547f3f6dbec11bf2eadfaf53ad8ab51b5.json
generated
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT network_key FROM account WHERE id = 0",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "network_key",
|
||||
"type_info": "Bytea"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "f7d2dae84613bcef330f7403352cc96547f3f6dbec11bf2eadfaf53ad8ab51b5"
|
||||
}
|
||||
62
backend/.sqlx/query-fe6e4f09f3028e5b6b6259e86cbad285680ce157aae9d7837ac020c8b2945e7f.json
generated
Normal file
62
backend/.sqlx/query-fe6e4f09f3028e5b6b6259e86cbad285680ce157aae9d7837ac020c8b2945e7f.json
generated
Normal file
@@ -0,0 +1,62 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT * FROM account WHERE id = 0",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "password",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "tor_key",
|
||||
"type_info": "Bytea"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "server_id",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "hostname",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "network_key",
|
||||
"type_info": "Bytea"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "root_ca_key_pem",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "root_ca_cert_pem",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "fe6e4f09f3028e5b6b6259e86cbad285680ce157aae9d7837ac020c8b2945e7f"
|
||||
}
|
||||
2032
backend/Cargo.lock
generated
2032
backend/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -14,7 +14,7 @@ keywords = [
|
||||
name = "start-os"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/Start9Labs/start-os"
|
||||
version = "0.3.4-rev.4"
|
||||
version = "0.3.5"
|
||||
|
||||
[lib]
|
||||
name = "startos"
|
||||
@@ -26,145 +26,150 @@ path = "src/main.rs"
|
||||
|
||||
[features]
|
||||
avahi = ["avahi-sys"]
|
||||
default = ["cli", "sdk", "daemon", "js_engine"]
|
||||
dev = []
|
||||
unstable = ["patch-db/unstable"]
|
||||
avahi-alias = ["avahi"]
|
||||
cli = []
|
||||
sdk = []
|
||||
daemon = []
|
||||
default = ["cli", "sdk", "daemon", "js_engine"]
|
||||
dev = []
|
||||
podman = []
|
||||
sdk = []
|
||||
unstable = ["console-subscriber", "tokio/tracing"]
|
||||
|
||||
[dependencies]
|
||||
aes = { version = "0.7.5", features = ["ctr"] }
|
||||
async-compression = { version = "0.3.15", features = [
|
||||
async-compression = { version = "0.4.4", features = [
|
||||
"gzip",
|
||||
"brotli",
|
||||
"tokio",
|
||||
] }
|
||||
async-stream = "0.3.3"
|
||||
async-trait = "0.1.56"
|
||||
async-stream = "0.3.5"
|
||||
async-trait = "0.1.74"
|
||||
avahi-sys = { git = "https://github.com/Start9Labs/avahi-sys", version = "0.10.0", branch = "feature/dynamic-linking", features = [
|
||||
"dynamic",
|
||||
], optional = true }
|
||||
base32 = "0.4.0"
|
||||
base64 = "0.13.0"
|
||||
base64ct = "1.5.1"
|
||||
base64 = "0.21.4"
|
||||
base64ct = "1.6.0"
|
||||
basic-cookies = "0.1.4"
|
||||
bimap = { version = "0.6.2", features = ["serde"] }
|
||||
bytes = "1"
|
||||
chrono = { version = "0.4.19", features = ["serde"] }
|
||||
clap = "3.2.8"
|
||||
color-eyre = "0.6.1"
|
||||
cookie = "0.16.2"
|
||||
cookie_store = "0.19.0"
|
||||
chrono = { version = "0.4.31", features = ["serde"] }
|
||||
clap = "3.2.25"
|
||||
color-eyre = "0.6.2"
|
||||
console = "0.15.7"
|
||||
console-subscriber = { version = "0.2", optional = true }
|
||||
cookie = "0.18.0"
|
||||
cookie_store = "0.20.0"
|
||||
current_platform = "0.2.0"
|
||||
digest = "0.10.3"
|
||||
digest-old = { package = "digest", version = "0.9.0" }
|
||||
digest = "0.10.7"
|
||||
divrem = "1.0.0"
|
||||
ed25519 = { version = "1.5.2", features = ["pkcs8", "pem", "alloc"] }
|
||||
ed25519-dalek = { version = "1.0.1", features = ["serde"] }
|
||||
ed25519 = { version = "2.2.3", features = ["pkcs8", "pem", "alloc"] }
|
||||
ed25519-dalek = { version = "2.0.0", features = [
|
||||
"serde",
|
||||
"hazmat",
|
||||
"zeroize",
|
||||
"rand_core",
|
||||
"digest",
|
||||
] }
|
||||
embassy_container_init = { path = "../libs/embassy_container_init" }
|
||||
emver = { version = "0.1.7", git = "https://github.com/Start9Labs/emver-rs.git", features = [
|
||||
"serde",
|
||||
] }
|
||||
fd-lock-rs = "0.1.4"
|
||||
futures = "0.3.21"
|
||||
futures = "0.3.28"
|
||||
git-version = "0.3.5"
|
||||
gpt = "3.0.0"
|
||||
gpt = "3.1.0"
|
||||
helpers = { path = "../libs/helpers" }
|
||||
embassy_container_init = { path = "../libs/embassy_container_init" }
|
||||
hex = "0.4.3"
|
||||
hmac = "0.12.1"
|
||||
http = "0.2.8"
|
||||
hyper = { version = "0.14.20", features = ["full"] }
|
||||
hyper-ws-listener = "0.2.0"
|
||||
http = "0.2.9"
|
||||
hyper = { version = "0.14.27", features = ["full"] }
|
||||
hyper-ws-listener = "0.3.0"
|
||||
id-pool = { version = "0.2.2", features = [
|
||||
"u16",
|
||||
"serde",
|
||||
], default-features = false }
|
||||
imbl = "2.0.0"
|
||||
imbl = "2.0.2"
|
||||
imbl-value = { git = "https://github.com/Start9Labs/imbl-value.git" }
|
||||
include_dir = "0.7.3"
|
||||
indexmap = { version = "1.9.1", features = ["serde"] }
|
||||
ipnet = { version = "2.7.1", features = ["serde"] }
|
||||
indexmap = { version = "2.0.2", features = ["serde"] }
|
||||
indicatif = { version = "0.17.7", features = ["tokio"] }
|
||||
ipnet = { version = "2.8.0", features = ["serde"] }
|
||||
iprange = { version = "0.6.7", features = ["serde"] }
|
||||
isocountry = "0.3.2"
|
||||
itertools = "0.10.3"
|
||||
jaq-core = "0.10.0"
|
||||
itertools = "0.11.0"
|
||||
jaq-core = "0.10.1"
|
||||
jaq-std = "0.10.0"
|
||||
josekit = "0.8.1"
|
||||
josekit = "0.8.4"
|
||||
js_engine = { path = '../libs/js_engine', optional = true }
|
||||
jsonpath_lib = { git = "https://github.com/Start9Labs/jsonpath.git" }
|
||||
lazy_static = "1.4.0"
|
||||
libc = "0.2.126"
|
||||
log = "0.4.17"
|
||||
mbrman = "0.5.0"
|
||||
libc = "0.2.149"
|
||||
log = "0.4.20"
|
||||
mbrman = "0.5.2"
|
||||
models = { version = "*", path = "../libs/models" }
|
||||
new_mime_guess = "4"
|
||||
nix = "0.25.0"
|
||||
nom = "7.1.1"
|
||||
num = "0.4.0"
|
||||
num_enum = "0.5.7"
|
||||
openssh-keys = "0.5.0"
|
||||
openssl = { version = "0.10.41", features = ["vendored"] }
|
||||
nix = { version = "0.27.1", features = ["user", "process", "signal", "fs"] }
|
||||
nom = "7.1.3"
|
||||
num = "0.4.1"
|
||||
num_enum = "0.7.0"
|
||||
openssh-keys = "0.6.2"
|
||||
openssl = { version = "0.10.57", features = ["vendored"] }
|
||||
p256 = { version = "0.13.2", features = ["pem"] }
|
||||
patch-db = { version = "*", path = "../patch-db/patch-db", features = [
|
||||
"trace",
|
||||
] }
|
||||
p256 = { version = "0.12.0", features = ["pem"] }
|
||||
pbkdf2 = "0.11.0"
|
||||
pin-project = "1.0.11"
|
||||
pkcs8 = { version = "0.9.0", features = ["std"] }
|
||||
pbkdf2 = "0.12.2"
|
||||
pin-project = "1.1.3"
|
||||
pkcs8 = { version = "0.10.2", features = ["std"] }
|
||||
prettytable-rs = "0.10.0"
|
||||
proptest = "1.0.0"
|
||||
proptest-derive = "0.3.0"
|
||||
proptest = "1.3.1"
|
||||
proptest-derive = "0.4.0"
|
||||
rand = { version = "0.8.5", features = ["std"] }
|
||||
rand-old = { package = "rand", version = "0.7.3" }
|
||||
regex = "1.6.0"
|
||||
reqwest = { version = "0.11.11", features = ["stream", "json", "socks"] }
|
||||
reqwest_cookie_store = "0.5.0"
|
||||
rpassword = "7.0.0"
|
||||
regex = "1.10.2"
|
||||
reqwest = { version = "0.11.22", features = ["stream", "json", "socks"] }
|
||||
reqwest_cookie_store = "0.6.0"
|
||||
rpassword = "7.2.0"
|
||||
rpc-toolkit = "0.2.2"
|
||||
rust-argon2 = "1.0.0"
|
||||
rust-argon2 = "2.0.0"
|
||||
scopeguard = "1.1" # because avahi-sys fucks your shit up
|
||||
serde = { version = "1.0.139", features = ["derive", "rc"] }
|
||||
serde_cbor = { package = "ciborium", version = "0.2.0" }
|
||||
serde_json = "1.0.93"
|
||||
serde_toml = { package = "toml", version = "0.5.9" }
|
||||
serde_with = { version = "2.0.1", features = ["macros", "json"] }
|
||||
serde_yaml = "0.9.11"
|
||||
serde = { version = "1.0", features = ["derive", "rc"] }
|
||||
serde_cbor = { package = "ciborium", version = "0.2.1" }
|
||||
serde_json = "1.0"
|
||||
serde_toml = { package = "toml", version = "0.8.2" }
|
||||
serde_with = { version = "3.4.0", features = ["macros", "json"] }
|
||||
serde_yaml = "0.9.25"
|
||||
sha2 = "0.10.2"
|
||||
sha2-old = { package = "sha2", version = "0.9.9" }
|
||||
simple-logging = "2.0.2"
|
||||
sqlx = { version = "0.6.0", features = [
|
||||
sqlx = { version = "0.7.2", features = [
|
||||
"chrono",
|
||||
"offline",
|
||||
"runtime-tokio-rustls",
|
||||
"postgres",
|
||||
] }
|
||||
ssh-key = { version = "0.5.1", features = ["ed25519"] }
|
||||
stderrlog = "0.5.3"
|
||||
tar = "0.4.38"
|
||||
thiserror = "1.0.31"
|
||||
tokio = { version = "1.23", features = ["full"] }
|
||||
tokio-stream = { version = "0.1.11", features = ["io-util", "sync", "net"] }
|
||||
tokio-tar = { git = "https://github.com/dr-bonez/tokio-tar.git" }
|
||||
tokio-tungstenite = { version = "0.17.1", features = ["native-tls"] }
|
||||
tokio-rustls = "0.23.4"
|
||||
sscanf = "0.4.1"
|
||||
ssh-key = { version = "0.6.2", features = ["ed25519"] }
|
||||
stderrlog = "0.5.4"
|
||||
tar = "0.4.40"
|
||||
thiserror = "1.0.49"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
tokio-rustls = "0.24.1"
|
||||
tokio-socks = "0.5.1"
|
||||
tokio-util = { version = "0.7.3", features = ["io"] }
|
||||
tokio-stream = { version = "0.1.14", features = ["io-util", "sync", "net"] }
|
||||
tokio-tar = { git = "https://github.com/dr-bonez/tokio-tar.git" }
|
||||
tokio-tungstenite = { version = "0.20.1", features = ["native-tls"] }
|
||||
tokio-util = { version = "0.7.9", features = ["io"] }
|
||||
torut = "0.2.1"
|
||||
tracing = "0.1.35"
|
||||
tracing = "0.1.39"
|
||||
tracing-error = "0.2.0"
|
||||
tracing-futures = "0.2.5"
|
||||
tracing-subscriber = { version = "0.3.14", features = ["env-filter"] }
|
||||
trust-dns-server = "0.22.0"
|
||||
typed-builder = "0.10.0"
|
||||
url = { version = "2.2.2", features = ["serde"] }
|
||||
urlencoding = "2.1.2"
|
||||
uuid = { version = "1.1.2", features = ["v4"] }
|
||||
zeroize = "1.5.7"
|
||||
indicatif = { version = "0.17.6", features = ["tokio"] }
|
||||
console = "^0.15"
|
||||
tracing-journald = "0.3.0"
|
||||
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
|
||||
trust-dns-server = "0.23.1"
|
||||
typed-builder = "0.17.0"
|
||||
url = { version = "2.4.1", features = ["serde"] }
|
||||
urlencoding = "2.1.3"
|
||||
uuid = { version = "1.4.1", features = ["v4"] }
|
||||
zeroize = "1.6.0"
|
||||
|
||||
[profile.test]
|
||||
opt-level = 3
|
||||
|
||||
@@ -22,49 +22,38 @@ if tty -s; then
|
||||
USE_TTY="-it"
|
||||
fi
|
||||
|
||||
alias 'rust-gnu-builder'='docker run $USE_TTY --rm -e "OS_ARCH=$OS_ARCH" -v "$HOME/.cargo/registry":/usr/local/cargo/registry -v "$(pwd)":/home/rust/src -w /home/rust/src -P start9/rust-arm-cross:aarch64'
|
||||
alias 'rust-musl-builder'='docker run $USE_TTY --rm -e "OS_ARCH=$OS_ARCH" -v "$HOME/.cargo/registry":/root/.cargo/registry -v "$(pwd)":/home/rust/src -P messense/rust-musl-cross:$ARCH-musl'
|
||||
|
||||
cd ..
|
||||
FLAGS=""
|
||||
RUSTFLAGS=""
|
||||
if [[ "$ENVIRONMENT" =~ (^|-)podman($|-) ]]; then
|
||||
FLAGS="podman,$FLAGS"
|
||||
fi
|
||||
if [[ "$ENVIRONMENT" =~ (^|-)unstable($|-) ]]; then
|
||||
FLAGS="unstable,$FLAGS"
|
||||
RUSTFLAGS="$RUSTFLAGS --cfg tokio_unstable"
|
||||
fi
|
||||
if [[ "$ENVIRONMENT" =~ (^|-)dev($|-) ]]; then
|
||||
FLAGS="dev,$FLAGS"
|
||||
fi
|
||||
|
||||
alias 'rust-gnu-builder'='docker run $USE_TTY --rm -e "OS_ARCH=$OS_ARCH" -e "RUSTFLAGS=$RUSTFLAGS" -v "$HOME/.cargo/registry":/usr/local/cargo/registry -v "$(pwd)":/home/rust/src -w /home/rust/src -P start9/rust-arm-cross:aarch64'
|
||||
alias 'rust-musl-builder'='docker run $USE_TTY --rm -e "OS_ARCH=$OS_ARCH" -v "$HOME/.cargo/registry":/root/.cargo/registry -v "$(pwd)":/home/rust/src -P messense/rust-musl-cross:$ARCH-musl'
|
||||
|
||||
set +e
|
||||
fail=
|
||||
if [[ "$FLAGS" = "" ]]; then
|
||||
rust-gnu-builder sh -c "(cd backend && cargo build --release --locked --features avahi-alias, --target=$ARCH-unknown-linux-gnu)"
|
||||
if test $? -ne 0; then
|
||||
fail=true
|
||||
fi
|
||||
for ARCH in x86_64 aarch64
|
||||
do
|
||||
rust-musl-builder sh -c "(cd libs && cargo build --release --locked --bin embassy_container_init )"
|
||||
if test $? -ne 0; then
|
||||
fail=true
|
||||
fi
|
||||
done
|
||||
else
|
||||
echo "FLAGS=$FLAGS"
|
||||
rust-gnu-builder sh -c "(cd backend && cargo build --release --features avahi-alias,$FLAGS --locked --target=$ARCH-unknown-linux-gnu)"
|
||||
if test $? -ne 0; then
|
||||
fail=true
|
||||
fi
|
||||
for ARCH in x86_64 aarch64
|
||||
do
|
||||
rust-musl-builder sh -c "(cd libs && cargo build --release --locked --bin embassy_container_init)"
|
||||
if test $? -ne 0; then
|
||||
fail=true
|
||||
fi
|
||||
done
|
||||
echo "FLAGS=\"$FLAGS\""
|
||||
echo "RUSTFLAGS=\"$RUSTFLAGS\""
|
||||
rust-gnu-builder sh -c "(cd backend && cargo build --release --features avahi-alias,$FLAGS --locked --target=$ARCH-unknown-linux-gnu)"
|
||||
if test $? -ne 0; then
|
||||
fail=true
|
||||
fi
|
||||
for ARCH in x86_64 aarch64
|
||||
do
|
||||
rust-musl-builder sh -c "(cd libs && cargo build --release --locked --bin embassy_container_init)"
|
||||
if test $? -ne 0; then
|
||||
fail=true
|
||||
fi
|
||||
done
|
||||
set -e
|
||||
cd backend
|
||||
|
||||
|
||||
@@ -1,744 +0,0 @@
|
||||
{
|
||||
"db": "PostgreSQL",
|
||||
"1ce5254f27de971fd87f5ab66d300f2b22433c86617a0dbf796bf2170186dd2e": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text",
|
||||
"Bytea"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "INSERT INTO network_keys (package, interface, key) VALUES ($1, $2, $3) ON CONFLICT (package, interface) DO NOTHING"
|
||||
},
|
||||
"21471490cdc3adb206274cc68e1ea745ffa5da4479478c1fd2158a45324b1930": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "DELETE FROM ssh_keys WHERE fingerprint = $1"
|
||||
},
|
||||
"28ea34bbde836e0618c5fc9bb7c36e463c20c841a7d6a0eb15be0f24f4a928ec": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "hostname",
|
||||
"ordinal": 0,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "path",
|
||||
"ordinal": 1,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "username",
|
||||
"ordinal": 2,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "password",
|
||||
"ordinal": 3,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int4"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "SELECT hostname, path, username, password FROM cifs_shares WHERE id = $1"
|
||||
},
|
||||
"4099028a5c0de578255bf54a67cef6cb0f1e9a4e158260700f1639dd4b438997": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "fingerprint",
|
||||
"ordinal": 0,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "openssh_pubkey",
|
||||
"ordinal": 1,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "created_at",
|
||||
"ordinal": 2,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "SELECT * FROM ssh_keys WHERE fingerprint = $1"
|
||||
},
|
||||
"4691e3a2ce80b59009ac17124f54f925f61dc5ea371903e62cdffa5d7b67ca96": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"ordinal": 0,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "logged_in",
|
||||
"ordinal": 1,
|
||||
"type_info": "Timestamp"
|
||||
},
|
||||
{
|
||||
"name": "logged_out",
|
||||
"ordinal": 2,
|
||||
"type_info": "Timestamp"
|
||||
},
|
||||
{
|
||||
"name": "last_active",
|
||||
"ordinal": 3,
|
||||
"type_info": "Timestamp"
|
||||
},
|
||||
{
|
||||
"name": "user_agent",
|
||||
"ordinal": 4,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "metadata",
|
||||
"ordinal": 5,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
}
|
||||
},
|
||||
"query": "SELECT * FROM session WHERE logged_out IS NULL OR logged_out > CURRENT_TIMESTAMP"
|
||||
},
|
||||
"4bcfbefb1eb3181343871a1cd7fc3afb81c2be5c681cfa8b4be0ce70610e9c3a": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "UPDATE session SET logged_out = CURRENT_TIMESTAMP WHERE id = $1"
|
||||
},
|
||||
"629be61c3c341c131ddbbff0293a83dbc6afd07cae69d246987f62cf0cc35c2a": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "password",
|
||||
"ordinal": 0,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
false
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
}
|
||||
},
|
||||
"query": "SELECT password FROM account"
|
||||
},
|
||||
"687688055e63d27123cdc89a5bbbd8361776290a9411d527eaf1fdb40bef399d": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "key",
|
||||
"ordinal": 0,
|
||||
"type_info": "Bytea"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
false
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "SELECT key FROM tor WHERE package = $1 AND interface = $2"
|
||||
},
|
||||
"6d35ccf780fb2bb62586dd1d3df9c1550a41ee580dad3f49d35cb843ebef10ca": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "UPDATE session SET last_active = CURRENT_TIMESTAMP WHERE id = $1 AND logged_out IS NULL OR logged_out > CURRENT_TIMESTAMP"
|
||||
},
|
||||
"770c1017734720453dc87b58c385b987c5af5807151ff71a59000014586752e0": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "key",
|
||||
"ordinal": 0,
|
||||
"type_info": "Bytea"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
false
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text",
|
||||
"Bytea"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "INSERT INTO network_keys (package, interface, key) VALUES ($1, $2, $3) ON CONFLICT (package, interface) DO UPDATE SET package = EXCLUDED.package RETURNING key"
|
||||
},
|
||||
"7b64f032d507e8ffe37c41f4c7ad514a66c421a11ab04c26d89a7aa8f6b67210": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"ordinal": 0,
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"name": "package_id",
|
||||
"ordinal": 1,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "created_at",
|
||||
"ordinal": 2,
|
||||
"type_info": "Timestamp"
|
||||
},
|
||||
{
|
||||
"name": "code",
|
||||
"ordinal": 3,
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"name": "level",
|
||||
"ordinal": 4,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "title",
|
||||
"ordinal": 5,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "message",
|
||||
"ordinal": 6,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "data",
|
||||
"ordinal": 7,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int4",
|
||||
"Int8"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "SELECT id, package_id, created_at, code, level, title, message, data FROM notifications WHERE id < $1 ORDER BY id DESC LIMIT $2"
|
||||
},
|
||||
"7c7a3549c997eb75bf964ea65fbb98a73045adf618696cd838d79203ef5383fb": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text",
|
||||
"Text",
|
||||
"Bytea",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "\n INSERT INTO account (\n id,\n server_id,\n hostname,\n password,\n network_key,\n root_ca_key_pem,\n root_ca_cert_pem\n ) VALUES (\n 0, $1, $2, $3, $4, $5, $6\n ) ON CONFLICT (id) DO UPDATE SET\n server_id = EXCLUDED.server_id,\n hostname = EXCLUDED.hostname,\n password = EXCLUDED.password,\n network_key = EXCLUDED.network_key,\n root_ca_key_pem = EXCLUDED.root_ca_key_pem,\n root_ca_cert_pem = EXCLUDED.root_ca_cert_pem\n "
|
||||
},
|
||||
"7e0649d839927e57fa03ee51a2c9f96a8bdb0fc97ee8a3c6df1069e1e2b98576": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "DELETE FROM tor WHERE package = $1"
|
||||
},
|
||||
"8951b9126fbf60dbb5997241e11e3526b70bccf3e407327917294a993bc17ed5": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text",
|
||||
"Bytea"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "INSERT INTO tor (package, interface, key) VALUES ($1, $2, $3) ON CONFLICT (package, interface) DO NOTHING"
|
||||
},
|
||||
"94d471bb374b4965c6cbedf8c17bbf6bea226d38efaf6559923c79a36d5ca08c": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"ordinal": 0,
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"name": "package_id",
|
||||
"ordinal": 1,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "created_at",
|
||||
"ordinal": 2,
|
||||
"type_info": "Timestamp"
|
||||
},
|
||||
{
|
||||
"name": "code",
|
||||
"ordinal": 3,
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"name": "level",
|
||||
"ordinal": 4,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "title",
|
||||
"ordinal": 5,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "message",
|
||||
"ordinal": 6,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "data",
|
||||
"ordinal": 7,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "SELECT id, package_id, created_at, code, level, title, message, data FROM notifications ORDER BY id DESC LIMIT $1"
|
||||
},
|
||||
"95c4ab4c645f3302568c6ff13d85ab58252362694cf0f56999bf60194d20583a": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"ordinal": 0,
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"name": "hostname",
|
||||
"ordinal": 1,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "path",
|
||||
"ordinal": 2,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "username",
|
||||
"ordinal": 3,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "password",
|
||||
"ordinal": 4,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
}
|
||||
},
|
||||
"query": "SELECT id, hostname, path, username, password FROM cifs_shares"
|
||||
},
|
||||
"a60d6e66719325b08dc4ecfacaf337527233c84eee758ac9be967906e5841d27": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int4"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "DELETE FROM cifs_shares WHERE id = $1"
|
||||
},
|
||||
"a6b0c8909a3a5d6d9156aebfb359424e6b5a1d1402e028219e21726f1ebd282e": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "fingerprint",
|
||||
"ordinal": 0,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "openssh_pubkey",
|
||||
"ordinal": 1,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "created_at",
|
||||
"ordinal": 2,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
}
|
||||
},
|
||||
"query": "SELECT fingerprint, openssh_pubkey, created_at FROM ssh_keys"
|
||||
},
|
||||
"b1147beaaabbed89f2ab8c1e13ec4393a9a8fde2833cf096af766a979d94dee6": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text",
|
||||
"Text",
|
||||
"Text",
|
||||
"Int4"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "UPDATE cifs_shares SET hostname = $1, path = $2, username = $3, password = $4 WHERE id = $5"
|
||||
},
|
||||
"d5117054072476377f3c4f040ea429d4c9b2cf534e76f35c80a2bf60e8599cca": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "openssh_pubkey",
|
||||
"ordinal": 0,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
false
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
}
|
||||
},
|
||||
"query": "SELECT openssh_pubkey FROM ssh_keys"
|
||||
},
|
||||
"da71f94b29798d1738d2b10b9a721ea72db8cfb362e7181c8226d9297507c62b": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Int4",
|
||||
"Text",
|
||||
"Text",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "INSERT INTO notifications (package_id, code, level, title, message, data) VALUES ($1, $2, $3, $4, $5, $6)"
|
||||
},
|
||||
"e185203cf84e43b801dfb23b4159e34aeaef1154dcd3d6811ab504915497ccf7": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int4"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "DELETE FROM notifications WHERE id = $1"
|
||||
},
|
||||
"e545696735f202f9d13cf22a561f3ff3f9aed7f90027a9ba97634bcb47d772f0": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "tor_key",
|
||||
"ordinal": 0,
|
||||
"type_info": "Bytea"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
true
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
}
|
||||
},
|
||||
"query": "SELECT tor_key FROM account WHERE id = 0"
|
||||
},
|
||||
"e5843c5b0e7819b29aa1abf2266799bd4f82e761837b526a0972c3d4439a264d": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "INSERT INTO session (id, user_agent, metadata) VALUES ($1, $2, $3)"
|
||||
},
|
||||
"e95322a8e2ae3b93f1e974b24c0b81803f1e9ec9e8ebbf15cafddfc1c5a028ed": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "package",
|
||||
"ordinal": 0,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "interface",
|
||||
"ordinal": 1,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "key",
|
||||
"ordinal": 2,
|
||||
"type_info": "Bytea"
|
||||
},
|
||||
{
|
||||
"name": "tor_key?",
|
||||
"ordinal": 3,
|
||||
"type_info": "Bytea"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "\n SELECT\n network_keys.package,\n network_keys.interface,\n network_keys.key,\n tor.key AS \"tor_key?\"\n FROM\n network_keys\n LEFT JOIN\n tor\n ON\n network_keys.package = tor.package\n AND\n network_keys.interface = tor.interface\n WHERE\n network_keys.package = $1\n "
|
||||
},
|
||||
"eb750adaa305bdbf3c5b70aaf59139c7b7569602adb58f2d6b3a94da4f167b0a": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int4"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "DELETE FROM notifications WHERE id < $1"
|
||||
},
|
||||
"ecc765d8205c0876956f95f76944ac6a5f34dd820c4073b7728c7067aab9fded": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"ordinal": 0,
|
||||
"type_info": "Int4"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
false
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "INSERT INTO cifs_shares (hostname, path, username, password) VALUES ($1, $2, $3, $4) RETURNING id"
|
||||
},
|
||||
"f6d1c5ef0f9d9577bea8382318967b9deb46da75788c7fe6082b43821c22d556": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "INSERT INTO ssh_keys (fingerprint, openssh_pubkey, created_at) VALUES ($1, $2, $3)"
|
||||
},
|
||||
"f7d2dae84613bcef330f7403352cc96547f3f6dbec11bf2eadfaf53ad8ab51b5": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "network_key",
|
||||
"ordinal": 0,
|
||||
"type_info": "Bytea"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
false
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
}
|
||||
},
|
||||
"query": "SELECT network_key FROM account WHERE id = 0"
|
||||
},
|
||||
"fe6e4f09f3028e5b6b6259e86cbad285680ce157aae9d7837ac020c8b2945e7f": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"ordinal": 0,
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"name": "password",
|
||||
"ordinal": 1,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "tor_key",
|
||||
"ordinal": 2,
|
||||
"type_info": "Bytea"
|
||||
},
|
||||
{
|
||||
"name": "server_id",
|
||||
"ordinal": 3,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "hostname",
|
||||
"ordinal": 4,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "network_key",
|
||||
"ordinal": 5,
|
||||
"type_info": "Bytea"
|
||||
},
|
||||
{
|
||||
"name": "root_ca_key_pem",
|
||||
"ordinal": 6,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "root_ca_cert_pem",
|
||||
"ordinal": 7,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
}
|
||||
},
|
||||
"query": "SELECT * FROM account WHERE id = 0"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
use ed25519_dalek::{ExpandedSecretKey, SecretKey};
|
||||
use models::ResultExt;
|
||||
use digest::Digest;
|
||||
use ed25519_dalek::SecretKey;
|
||||
use openssl::pkey::{PKey, Private};
|
||||
use openssl::x509::X509;
|
||||
use sqlx::PgExecutor;
|
||||
@@ -7,7 +7,8 @@ use sqlx::PgExecutor;
|
||||
use crate::hostname::{generate_hostname, generate_id, Hostname};
|
||||
use crate::net::keys::Key;
|
||||
use crate::net::ssl::{generate_key, make_root_cert};
|
||||
use crate::Error;
|
||||
use crate::prelude::*;
|
||||
use crate::util::crypto::ed25519_expand_key;
|
||||
|
||||
fn hash_password(password: &str) -> Result<String, Error> {
|
||||
argon2::hash_encoded(
|
||||
@@ -51,13 +52,23 @@ impl AccountInfo {
|
||||
let server_id = r.server_id.unwrap_or_else(generate_id);
|
||||
let hostname = r.hostname.map(Hostname).unwrap_or_else(generate_hostname);
|
||||
let password = r.password;
|
||||
let network_key = SecretKey::from_bytes(&r.network_key)?;
|
||||
let network_key = SecretKey::try_from(r.network_key).map_err(|e| {
|
||||
Error::new(
|
||||
eyre!("expected vec of len 32, got len {}", e.len()),
|
||||
ErrorKind::ParseDbField,
|
||||
)
|
||||
})?;
|
||||
let tor_key = if let Some(k) = &r.tor_key {
|
||||
ExpandedSecretKey::from_bytes(k)?
|
||||
<[u8; 64]>::try_from(&k[..]).map_err(|_| {
|
||||
Error::new(
|
||||
eyre!("expected vec of len 64, got len {}", k.len()),
|
||||
ErrorKind::ParseDbField,
|
||||
)
|
||||
})?
|
||||
} else {
|
||||
ExpandedSecretKey::from(&network_key)
|
||||
ed25519_expand_key(&network_key)
|
||||
};
|
||||
let key = Key::from_pair(None, network_key.to_bytes(), tor_key.to_bytes());
|
||||
let key = Key::from_pair(None, network_key, tor_key);
|
||||
let root_ca_key = PKey::private_key_from_pem(r.root_ca_key_pem.as_bytes())?;
|
||||
let root_ca_cert = X509::from_pem(r.root_ca_cert_pem.as_bytes())?;
|
||||
|
||||
|
||||
@@ -134,7 +134,7 @@ pub async fn action(
|
||||
let manifest = ctx
|
||||
.db
|
||||
.peek()
|
||||
.await?
|
||||
.await
|
||||
.as_package_data()
|
||||
.as_idx(&pkg_id)
|
||||
.or_not_found(&pkg_id)?
|
||||
|
||||
@@ -160,7 +160,7 @@ pub async fn login(
|
||||
) -> Result<(), Error> {
|
||||
let password = password.unwrap_or_default().decrypt(&ctx)?;
|
||||
let mut handle = ctx.secret_store.acquire().await?;
|
||||
check_password_against_db(&mut handle, &password).await?;
|
||||
check_password_against_db(handle.as_mut(), &password).await?;
|
||||
|
||||
let hash_token = HashSessionToken::new();
|
||||
let user_agent = req.headers.get("user-agent").and_then(|h| h.to_str().ok());
|
||||
@@ -172,7 +172,7 @@ pub async fn login(
|
||||
user_agent,
|
||||
metadata,
|
||||
)
|
||||
.execute(&mut handle)
|
||||
.execute(handle.as_mut())
|
||||
.await?;
|
||||
res.headers.insert(
|
||||
"set-cookie",
|
||||
@@ -263,7 +263,7 @@ pub async fn list(
|
||||
sessions: sqlx::query!(
|
||||
"SELECT * FROM session WHERE logged_out IS NULL OR logged_out > CURRENT_TIMESTAMP"
|
||||
)
|
||||
.fetch_all(&mut ctx.secret_store.acquire().await?)
|
||||
.fetch_all(ctx.secret_store.acquire().await?.as_mut())
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|row| {
|
||||
|
||||
@@ -56,16 +56,16 @@ pub async fn backup_all(
|
||||
package_ids: Option<OrdSet<PackageId>>,
|
||||
#[arg] password: crate::auth::PasswordType,
|
||||
) -> Result<(), Error> {
|
||||
let db = ctx.db.peek().await?;
|
||||
let db = ctx.db.peek().await;
|
||||
let old_password_decrypted = old_password
|
||||
.as_ref()
|
||||
.unwrap_or(&password)
|
||||
.clone()
|
||||
.decrypt(&ctx)?;
|
||||
let password = password.decrypt(&ctx)?;
|
||||
check_password_against_db(&mut ctx.secret_store.acquire().await?, &password).await?;
|
||||
check_password_against_db(ctx.secret_store.acquire().await?.as_mut(), &password).await?;
|
||||
let fs = target_id
|
||||
.load(&mut ctx.secret_store.acquire().await?)
|
||||
.load(ctx.secret_store.acquire().await?.as_mut())
|
||||
.await?;
|
||||
let mut backup_guard = BackupMountGuard::mount(
|
||||
TmpMountGuard::mount(&fs, ReadWrite).await?,
|
||||
@@ -265,7 +265,7 @@ async fn perform_backup(
|
||||
}
|
||||
}
|
||||
|
||||
let ui = ctx.db.peek().await?.into_ui().de()?;
|
||||
let ui = ctx.db.peek().await.into_ui().de()?;
|
||||
|
||||
let mut os_backup_file = AtomicFile::new(
|
||||
backup_guard.lock().await.as_ref().join("os-backup.cbor"),
|
||||
|
||||
@@ -134,7 +134,7 @@ impl BackupActions {
|
||||
let marketplace_url = ctx
|
||||
.db
|
||||
.peek()
|
||||
.await?
|
||||
.await
|
||||
.as_package_data()
|
||||
.as_idx(&pkg_id)
|
||||
.or_not_found(pkg_id)?
|
||||
|
||||
@@ -52,7 +52,7 @@ pub async fn restore_packages_rpc(
|
||||
#[arg] password: String,
|
||||
) -> Result<(), Error> {
|
||||
let fs = target_id
|
||||
.load(&mut ctx.secret_store.acquire().await?)
|
||||
.load(ctx.secret_store.acquire().await?.as_mut())
|
||||
.await?;
|
||||
let backup_guard =
|
||||
BackupMountGuard::mount(TmpMountGuard::mount(&fs, ReadWrite).await?, &password).await?;
|
||||
@@ -310,7 +310,7 @@ async fn assure_restoring(
|
||||
let mut insert_packages = BTreeMap::new();
|
||||
|
||||
for id in ids {
|
||||
let peek = ctx.db.peek().await?;
|
||||
let peek = ctx.db.peek().await;
|
||||
|
||||
let model = peek.as_package_data().as_idx(&id);
|
||||
|
||||
@@ -402,7 +402,7 @@ async fn restore_package<'a>(
|
||||
iface.to_string(),
|
||||
k,
|
||||
)
|
||||
.execute(&mut secrets_tx).await?;
|
||||
.execute(secrets_tx.as_mut()).await?;
|
||||
}
|
||||
// DEPRECATED
|
||||
for (iface, key) in metadata.tor_keys {
|
||||
@@ -413,7 +413,7 @@ async fn restore_package<'a>(
|
||||
iface.to_string(),
|
||||
k,
|
||||
)
|
||||
.execute(&mut secrets_tx).await?;
|
||||
.execute(secrets_tx.as_mut()).await?;
|
||||
}
|
||||
secrets_tx.commit().await?;
|
||||
drop(secrets);
|
||||
|
||||
@@ -142,7 +142,7 @@ pub async fn list(
|
||||
let mut sql_handle = ctx.secret_store.acquire().await?;
|
||||
let (disks_res, cifs) = tokio::try_join!(
|
||||
crate::disk::util::list(&ctx.os_partitions),
|
||||
cifs::list(&mut sql_handle),
|
||||
cifs::list(sql_handle.as_mut()),
|
||||
)?;
|
||||
Ok(disks_res
|
||||
.into_iter()
|
||||
@@ -233,7 +233,7 @@ pub async fn info(
|
||||
let guard = BackupMountGuard::mount(
|
||||
TmpMountGuard::mount(
|
||||
&target_id
|
||||
.load(&mut ctx.secret_store.acquire().await?)
|
||||
.load(ctx.secret_store.acquire().await?.as_mut())
|
||||
.await?,
|
||||
ReadWrite,
|
||||
)
|
||||
@@ -271,7 +271,7 @@ pub async fn mount(
|
||||
TmpMountGuard::mount(
|
||||
&target_id
|
||||
.clone()
|
||||
.load(&mut ctx.secret_store.acquire().await?)
|
||||
.load(ctx.secret_store.acquire().await?.as_mut())
|
||||
.await?,
|
||||
ReadWrite,
|
||||
)
|
||||
|
||||
@@ -5,6 +5,8 @@ pub mod avahi_alias;
|
||||
pub mod deprecated;
|
||||
#[cfg(feature = "cli")]
|
||||
pub mod start_cli;
|
||||
#[cfg(feature = "js_engine")]
|
||||
pub mod start_deno;
|
||||
#[cfg(feature = "daemon")]
|
||||
pub mod start_init;
|
||||
#[cfg(feature = "sdk")]
|
||||
@@ -16,6 +18,8 @@ fn select_executable(name: &str) -> Option<fn()> {
|
||||
match name {
|
||||
#[cfg(feature = "avahi-alias")]
|
||||
"avahi-alias" => Some(avahi_alias::main),
|
||||
#[cfg(feature = "js_engine")]
|
||||
"start-deno" => Some(start_deno::main),
|
||||
#[cfg(feature = "cli")]
|
||||
"start-cli" => Some(start_cli::main),
|
||||
#[cfg(feature = "sdk")]
|
||||
|
||||
145
backend/src/bins/start_deno.rs
Normal file
145
backend/src/bins/start_deno.rs
Normal file
@@ -0,0 +1,145 @@
|
||||
use clap::Arg;
|
||||
use rpc_toolkit::command;
|
||||
use rpc_toolkit::run_cli;
|
||||
use rpc_toolkit::yajrc::RpcError;
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::context::CliContext;
|
||||
use crate::procedure::js_scripts::ExecuteArgs;
|
||||
use crate::s9pk::manifest::PackageId;
|
||||
use crate::util::logger::EmbassyLogger;
|
||||
use crate::util::serde::{display_serializable, parse_stdin_deserializable};
|
||||
use crate::version::{Current, VersionT};
|
||||
use crate::Error;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref VERSION_STRING: String = Current::new().semver().to_string();
|
||||
}
|
||||
|
||||
#[command(subcommands(execute, sandbox))]
|
||||
fn deno_api() -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[command(cli_only, display(display_serializable))]
|
||||
async fn execute(
|
||||
#[arg(stdin, parse(parse_stdin_deserializable))] arg: ExecuteArgs,
|
||||
) -> Result<Result<Value, (i32, String)>, Error> {
|
||||
let ExecuteArgs {
|
||||
procedure,
|
||||
directory,
|
||||
pkg_id,
|
||||
pkg_version,
|
||||
name,
|
||||
volumes,
|
||||
input,
|
||||
} = arg;
|
||||
PackageLogger::init(&pkg_id);
|
||||
procedure
|
||||
.execute_impl(&directory, &pkg_id, &pkg_version, name, &volumes, input)
|
||||
.await
|
||||
}
|
||||
#[command(cli_only, display(display_serializable))]
|
||||
async fn sandbox(
|
||||
#[arg(stdin, parse(parse_stdin_deserializable))] arg: ExecuteArgs,
|
||||
) -> Result<Result<Value, (i32, String)>, Error> {
|
||||
let ExecuteArgs {
|
||||
procedure,
|
||||
directory,
|
||||
pkg_id,
|
||||
pkg_version,
|
||||
name,
|
||||
volumes,
|
||||
input,
|
||||
} = arg;
|
||||
PackageLogger::init(&pkg_id);
|
||||
procedure
|
||||
.sandboxed_impl(&directory, &pkg_id, &pkg_version, &volumes, input, name)
|
||||
.await
|
||||
}
|
||||
|
||||
use tracing::Subscriber;
|
||||
use tracing_subscriber::util::SubscriberInitExt;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct PackageLogger {}
|
||||
|
||||
impl PackageLogger {
|
||||
fn base_subscriber(id: &PackageId) -> impl Subscriber {
|
||||
use tracing_error::ErrorLayer;
|
||||
use tracing_subscriber::prelude::*;
|
||||
use tracing_subscriber::{fmt, EnvFilter};
|
||||
|
||||
let filter_layer = EnvFilter::builder()
|
||||
.with_default_directive(
|
||||
format!("{}=info", std::module_path!().split("::").next().unwrap())
|
||||
.parse()
|
||||
.unwrap(),
|
||||
)
|
||||
.from_env_lossy();
|
||||
let fmt_layer = fmt::layer().with_writer(std::io::stderr).with_target(true);
|
||||
let journald_layer = tracing_journald::layer()
|
||||
.unwrap()
|
||||
.with_syslog_identifier(format!("{id}.embassy"));
|
||||
|
||||
let sub = tracing_subscriber::registry()
|
||||
.with(filter_layer)
|
||||
.with(fmt_layer)
|
||||
.with(journald_layer)
|
||||
.with(ErrorLayer::default());
|
||||
|
||||
sub
|
||||
}
|
||||
pub fn init(id: &PackageId) -> Self {
|
||||
Self::base_subscriber(id).init();
|
||||
color_eyre::install().unwrap_or_else(|_| tracing::warn!("tracing too many times"));
|
||||
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
fn inner_main() -> Result<(), Error> {
|
||||
run_cli!({
|
||||
command: deno_api,
|
||||
app: app => app
|
||||
.name("StartOS Deno Executor")
|
||||
.version(&**VERSION_STRING)
|
||||
.arg(
|
||||
clap::Arg::with_name("config")
|
||||
.short('c')
|
||||
.long("config")
|
||||
.takes_value(true),
|
||||
),
|
||||
context: matches => {
|
||||
CliContext::init(matches)?
|
||||
},
|
||||
exit: |e: RpcError| {
|
||||
match e.data {
|
||||
Some(Value::String(s)) => eprintln!("{}: {}", e.message, s),
|
||||
Some(Value::Object(o)) => if let Some(Value::String(s)) = o.get("details") {
|
||||
eprintln!("{}: {}", e.message, s);
|
||||
if let Some(Value::String(s)) = o.get("debug") {
|
||||
tracing::debug!("{}", s)
|
||||
}
|
||||
}
|
||||
Some(a) => eprintln!("{}: {}", e.message, a),
|
||||
None => eprintln!("{}", e.message),
|
||||
}
|
||||
|
||||
std::process::exit(e.code);
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
match inner_main() {
|
||||
Ok(_) => (),
|
||||
Err(e) => {
|
||||
eprintln!("{}", e.source);
|
||||
tracing::debug!("{:?}", e.source);
|
||||
drop(e.source);
|
||||
std::process::exit(e.kind as i32)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ use crate::context::{DiagnosticContext, InstallContext, SetupContext};
|
||||
use crate::disk::fsck::RepairStrategy;
|
||||
use crate::disk::main::DEFAULT_PASSWORD;
|
||||
use crate::disk::REPAIR_DISK_PATH;
|
||||
use crate::firmware::update_firmware;
|
||||
use crate::init::STANDBY_MODE_PATH;
|
||||
use crate::net::web_server::WebServer;
|
||||
use crate::shutdown::Shutdown;
|
||||
@@ -19,7 +20,14 @@ use crate::util::Invoke;
|
||||
use crate::{Error, ErrorKind, ResultExt, OS_ARCH};
|
||||
|
||||
#[instrument(skip_all)]
|
||||
async fn setup_or_init(cfg_path: Option<PathBuf>) -> Result<(), Error> {
|
||||
async fn setup_or_init(cfg_path: Option<PathBuf>) -> Result<Option<Shutdown>, Error> {
|
||||
if update_firmware().await?.0 {
|
||||
return Ok(Some(Shutdown {
|
||||
export_args: None,
|
||||
restart: true,
|
||||
}));
|
||||
}
|
||||
|
||||
Command::new("ln")
|
||||
.arg("-sf")
|
||||
.arg("/usr/lib/embassy/scripts/fake-apt")
|
||||
@@ -146,7 +154,7 @@ async fn setup_or_init(cfg_path: Option<PathBuf>) -> Result<(), Error> {
|
||||
crate::init::init(&cfg).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
async fn run_script_if_exists<P: AsRef<Path>>(path: P) {
|
||||
@@ -180,46 +188,47 @@ async fn inner_main(cfg_path: Option<PathBuf>) -> Result<Option<Shutdown>, Error
|
||||
|
||||
run_script_if_exists("/media/embassy/config/preinit.sh").await;
|
||||
|
||||
let res = if let Err(e) = setup_or_init(cfg_path.clone()).await {
|
||||
async move {
|
||||
tracing::error!("{}", e.source);
|
||||
tracing::debug!("{}", e.source);
|
||||
crate::sound::BEETHOVEN.play().await?;
|
||||
let res = match setup_or_init(cfg_path.clone()).await {
|
||||
Err(e) => {
|
||||
async move {
|
||||
tracing::error!("{}", e.source);
|
||||
tracing::debug!("{}", e.source);
|
||||
crate::sound::BEETHOVEN.play().await?;
|
||||
|
||||
let ctx = DiagnosticContext::init(
|
||||
cfg_path,
|
||||
if tokio::fs::metadata("/media/embassy/config/disk.guid")
|
||||
.await
|
||||
.is_ok()
|
||||
{
|
||||
Some(Arc::new(
|
||||
tokio::fs::read_to_string("/media/embassy/config/disk.guid") // unique identifier for volume group - keeps track of the disk that goes with your embassy
|
||||
.await?
|
||||
.trim()
|
||||
.to_owned(),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
e,
|
||||
)
|
||||
.await?;
|
||||
let ctx = DiagnosticContext::init(
|
||||
cfg_path,
|
||||
if tokio::fs::metadata("/media/embassy/config/disk.guid")
|
||||
.await
|
||||
.is_ok()
|
||||
{
|
||||
Some(Arc::new(
|
||||
tokio::fs::read_to_string("/media/embassy/config/disk.guid") // unique identifier for volume group - keeps track of the disk that goes with your embassy
|
||||
.await?
|
||||
.trim()
|
||||
.to_owned(),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
e,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let server = WebServer::diagnostic(
|
||||
SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 80),
|
||||
ctx.clone(),
|
||||
)
|
||||
.await?;
|
||||
let server = WebServer::diagnostic(
|
||||
SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 80),
|
||||
ctx.clone(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let shutdown = ctx.shutdown.subscribe().recv().await.unwrap();
|
||||
let shutdown = ctx.shutdown.subscribe().recv().await.unwrap();
|
||||
|
||||
server.shutdown().await;
|
||||
server.shutdown().await;
|
||||
|
||||
Ok(shutdown)
|
||||
Ok(shutdown)
|
||||
}
|
||||
.await
|
||||
}
|
||||
.await
|
||||
} else {
|
||||
Ok(None)
|
||||
Ok(s) => Ok(s),
|
||||
};
|
||||
|
||||
run_script_if_exists("/media/embassy/config/postinit.sh").await;
|
||||
|
||||
@@ -16,7 +16,7 @@ use crate::{Error, ErrorKind, ResultExt};
|
||||
|
||||
#[instrument(skip_all)]
|
||||
async fn inner_main(cfg_path: Option<PathBuf>) -> Result<Option<Shutdown>, Error> {
|
||||
let (rpc_ctx, server, shutdown) = {
|
||||
let (rpc_ctx, server, shutdown) = async {
|
||||
let rpc_ctx = RpcContext::init(
|
||||
cfg_path,
|
||||
Arc::new(
|
||||
@@ -91,8 +91,9 @@ async fn inner_main(cfg_path: Option<PathBuf>) -> Result<Option<Shutdown>, Error
|
||||
|
||||
sig_handler.abort();
|
||||
|
||||
(rpc_ctx, server, shutdown)
|
||||
};
|
||||
Ok::<_, Error>((rpc_ctx, server, shutdown))
|
||||
}
|
||||
.await?;
|
||||
server.shutdown().await;
|
||||
rpc_ctx.shutdown().await?;
|
||||
|
||||
|
||||
@@ -14,9 +14,9 @@ use rpc_toolkit::command;
|
||||
use tracing::instrument;
|
||||
|
||||
use crate::context::RpcContext;
|
||||
use crate::db::model::CurrentDependencies;
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::s9pk::manifest::{Manifest, PackageId};
|
||||
use crate::s9pk::manifest::{PackageId};
|
||||
use crate::util::display_none;
|
||||
use crate::util::serde::{display_serializable, parse_stdin_deserializable, IoFormat};
|
||||
use crate::Error;
|
||||
@@ -167,7 +167,7 @@ pub async fn get(
|
||||
#[arg(long = "format")]
|
||||
format: Option<IoFormat>,
|
||||
) -> Result<ConfigRes, Error> {
|
||||
let db = ctx.db.peek().await?;
|
||||
let db = ctx.db.peek().await;
|
||||
let manifest = db
|
||||
.as_package_data()
|
||||
.as_idx(&id)
|
||||
@@ -256,7 +256,7 @@ pub async fn configure(
|
||||
id: &PackageId,
|
||||
configure_context: ConfigureContext,
|
||||
) -> Result<BTreeMap<PackageId, String>, Error> {
|
||||
let db = ctx.db.peek().await?;
|
||||
let db = ctx.db.peek().await;
|
||||
let package = db
|
||||
.as_package_data()
|
||||
.as_idx(id)
|
||||
|
||||
@@ -1696,7 +1696,6 @@ impl TorAddressPointer {
|
||||
.db
|
||||
.peek()
|
||||
.await
|
||||
.map_err(|e| ConfigurationError::SystemError(e))?
|
||||
.as_package_data()
|
||||
.as_idx(&self.package_id)
|
||||
.and_then(|pde| pde.as_installed())
|
||||
@@ -1739,7 +1738,6 @@ impl LanAddressPointer {
|
||||
.db
|
||||
.peek()
|
||||
.await
|
||||
.map_err(|e| ConfigurationError::SystemError(e))?
|
||||
.as_package_data()
|
||||
.as_idx(&self.package_id)
|
||||
.and_then(|pde| pde.as_installed())
|
||||
@@ -1775,11 +1773,7 @@ impl ConfigPointer {
|
||||
Ok(self.select(&Value::Object(cfg.clone())))
|
||||
} else {
|
||||
let id = &self.package_id;
|
||||
let db = ctx
|
||||
.db
|
||||
.peek()
|
||||
.await
|
||||
.map_err(|e| ConfigurationError::SystemError(e))?;
|
||||
let db = ctx.db.peek().await;
|
||||
let manifest = db.as_package_data().as_idx(id).map(|pde| pde.as_manifest());
|
||||
let cfg_actions = manifest.and_then(|m| m.as_config().transpose_ref());
|
||||
if let (Some(manifest), Some(cfg_actions)) = (manifest, cfg_actions) {
|
||||
@@ -1900,10 +1894,11 @@ impl TorKeyPointer {
|
||||
));
|
||||
}
|
||||
let key = Key::for_interface(
|
||||
&mut secrets
|
||||
secrets
|
||||
.acquire()
|
||||
.await
|
||||
.map_err(|e| ConfigurationError::SystemError(e.into()))?,
|
||||
.map_err(|e| ConfigurationError::SystemError(e.into()))?
|
||||
.as_mut(),
|
||||
Some((self.package_id.clone(), self.interface.clone())),
|
||||
)
|
||||
.await
|
||||
|
||||
@@ -6,8 +6,7 @@ use std::sync::Arc;
|
||||
|
||||
use clap::ArgMatches;
|
||||
use color_eyre::eyre::eyre;
|
||||
use cookie::Cookie;
|
||||
use cookie_store::CookieStore;
|
||||
use cookie_store::{CookieStore, RawCookie};
|
||||
use josekit::jwk::Jwk;
|
||||
use reqwest::Proxy;
|
||||
use reqwest_cookie_store::CookieStoreMutex;
|
||||
@@ -111,7 +110,10 @@ impl CliContext {
|
||||
};
|
||||
if let Ok(local) = std::fs::read_to_string(LOCAL_AUTH_COOKIE_PATH) {
|
||||
store
|
||||
.insert_raw(&Cookie::new("local", local), &"http://localhost".parse()?)
|
||||
.insert_raw(
|
||||
&RawCookie::new("local", local),
|
||||
&"http://localhost".parse()?,
|
||||
)
|
||||
.with_kind(crate::ErrorKind::Network)?;
|
||||
}
|
||||
store
|
||||
|
||||
@@ -22,6 +22,7 @@ use crate::account::AccountInfo;
|
||||
use crate::core::rpc_continuations::{RequestGuid, RestHandler, RpcContinuation};
|
||||
use crate::db::model::{CurrentDependents, Database, PackageDataEntryMatchModelRef};
|
||||
use crate::db::prelude::PatchDbExt;
|
||||
use crate::dependencies::compute_dependency_config_errs;
|
||||
use crate::disk::OsPartitionInfo;
|
||||
use crate::init::init_postgres;
|
||||
use crate::install::cleanup::{cleanup_failed, uninstall};
|
||||
@@ -155,8 +156,7 @@ impl RpcContext {
|
||||
.unwrap_or(SocketAddr::from(([127, 0, 0, 1], 9051))),
|
||||
tor_proxy,
|
||||
base.dns_bind
|
||||
.as_ref()
|
||||
.map(|v| v.as_slice())
|
||||
.as_deref()
|
||||
.unwrap_or(&[SocketAddr::from(([127, 0, 0, 1], 53))]),
|
||||
SslManager::new(&account)?,
|
||||
&account.hostname,
|
||||
@@ -217,11 +217,8 @@ impl RpcContext {
|
||||
});
|
||||
|
||||
let res = Self(seed.clone());
|
||||
res.cleanup().await?;
|
||||
res.cleanup_and_initialize().await?;
|
||||
tracing::info!("Cleaned up transient states");
|
||||
let peeked = res.db.peek().await?;
|
||||
res.managers.init(res.clone(), peeked).await?;
|
||||
tracing::info!("Initialized Package Managers");
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
@@ -236,7 +233,7 @@ impl RpcContext {
|
||||
}
|
||||
|
||||
#[instrument(skip(self))]
|
||||
pub async fn cleanup(&self) -> Result<(), Error> {
|
||||
pub async fn cleanup_and_initialize(&self) -> Result<(), Error> {
|
||||
self.db
|
||||
.mutate(|f| {
|
||||
let mut current_dependents = f
|
||||
@@ -278,9 +275,10 @@ impl RpcContext {
|
||||
Ok(())
|
||||
})
|
||||
.await?;
|
||||
let peek = self.db.peek().await?;
|
||||
|
||||
let peek = self.db.peek().await;
|
||||
|
||||
for (package_id, package) in peek.as_package_data().as_entries()?.into_iter() {
|
||||
let package = package.clone();
|
||||
let action = match package.as_match() {
|
||||
PackageDataEntryMatchModelRef::Installing(_)
|
||||
| PackageDataEntryMatchModelRef::Restoring(_)
|
||||
@@ -288,7 +286,12 @@ impl RpcContext {
|
||||
cleanup_failed(self, &package_id).await
|
||||
}
|
||||
PackageDataEntryMatchModelRef::Removing(_) => {
|
||||
uninstall(self, &mut self.secret_store.acquire().await?, &package_id).await
|
||||
uninstall(
|
||||
self,
|
||||
self.secret_store.acquire().await?.as_mut(),
|
||||
&package_id,
|
||||
)
|
||||
.await
|
||||
}
|
||||
PackageDataEntryMatchModelRef::Installed(m) => {
|
||||
let version = m.as_manifest().as_version().clone().de()?;
|
||||
@@ -298,7 +301,7 @@ impl RpcContext {
|
||||
&self.datadir,
|
||||
&package_id,
|
||||
&version,
|
||||
&volume_id,
|
||||
volume_id,
|
||||
))
|
||||
.with_kind(ErrorKind::Filesystem)?;
|
||||
if tokio::fs::metadata(&tmp_path).await.is_ok() {
|
||||
@@ -314,7 +317,8 @@ impl RpcContext {
|
||||
tracing::debug!("{:?}", e);
|
||||
}
|
||||
}
|
||||
self.db
|
||||
let peek = self
|
||||
.db
|
||||
.mutate(|v| {
|
||||
for (_, pde) in v.as_package_data_mut().as_entries_mut()? {
|
||||
let status = pde
|
||||
@@ -329,9 +333,49 @@ impl RpcContext {
|
||||
MainStatus::Stopped
|
||||
})?;
|
||||
}
|
||||
Ok(v.clone())
|
||||
})
|
||||
.await?;
|
||||
self.managers.init(self.clone(), peek.clone()).await?;
|
||||
tracing::info!("Initialized Package Managers");
|
||||
|
||||
let mut all_dependency_config_errs = BTreeMap::new();
|
||||
for (package_id, package) in peek.as_package_data().as_entries()?.into_iter() {
|
||||
let package = package.clone();
|
||||
if let Some(current_dependencies) = package
|
||||
.as_installed()
|
||||
.and_then(|x| x.as_current_dependencies().de().ok())
|
||||
{
|
||||
let manifest = package.as_manifest().de()?;
|
||||
all_dependency_config_errs.insert(
|
||||
package_id.clone(),
|
||||
compute_dependency_config_errs(
|
||||
self,
|
||||
&peek,
|
||||
&manifest,
|
||||
¤t_dependencies,
|
||||
&Default::default(),
|
||||
)
|
||||
.await?,
|
||||
);
|
||||
}
|
||||
}
|
||||
self.db
|
||||
.mutate(|v| {
|
||||
for (package_id, errs) in all_dependency_config_errs {
|
||||
if let Some(config_errors) = v
|
||||
.as_package_data_mut()
|
||||
.as_idx_mut(&package_id)
|
||||
.and_then(|pde| pde.as_installed_mut())
|
||||
.map(|i| i.as_status_mut().as_dependency_config_errors_mut())
|
||||
{
|
||||
config_errors.ser(&errs)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -389,7 +433,7 @@ impl RpcContext {
|
||||
}
|
||||
impl AsRef<Jwk> for RpcContext {
|
||||
fn as_ref(&self) -> &Jwk {
|
||||
&*CURRENT_SECRET
|
||||
&CURRENT_SECRET
|
||||
}
|
||||
}
|
||||
impl Context for RpcContext {}
|
||||
@@ -403,7 +447,7 @@ impl Deref for RpcContext {
|
||||
tracing_error::SpanTrace::capture()
|
||||
);
|
||||
}
|
||||
&*self.0
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
impl Drop for RpcContext {
|
||||
|
||||
@@ -7,8 +7,8 @@ use rpc_toolkit::Context;
|
||||
use serde::Deserialize;
|
||||
use tracing::instrument;
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::util::config::{load_config_from_paths, local_config_path};
|
||||
use crate::{Error, ResultExt};
|
||||
|
||||
#[derive(Debug, Default, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
@@ -50,21 +50,21 @@ impl SdkContext {
|
||||
}
|
||||
/// BLOCKING
|
||||
#[instrument(skip_all)]
|
||||
pub fn developer_key(&self) -> Result<ed25519_dalek::Keypair, Error> {
|
||||
pub fn developer_key(&self) -> Result<ed25519_dalek::SigningKey, Error> {
|
||||
if !self.developer_key_path.exists() {
|
||||
return Err(Error::new(eyre!("Developer Key does not exist! Please run `embassy-sdk init` before running this command."), crate::ErrorKind::Uninitialized));
|
||||
return Err(Error::new(eyre!("Developer Key does not exist! Please run `start-sdk init` before running this command."), crate::ErrorKind::Uninitialized));
|
||||
}
|
||||
let pair = <ed25519::KeypairBytes as ed25519::pkcs8::DecodePrivateKey>::from_pkcs8_pem(
|
||||
&std::fs::read_to_string(&self.developer_key_path)?,
|
||||
)
|
||||
.with_kind(crate::ErrorKind::Pem)?;
|
||||
let secret = ed25519_dalek::SecretKey::from_bytes(&pair.secret_key[..])?;
|
||||
let public = if let Some(public) = pair.public_key {
|
||||
ed25519_dalek::PublicKey::from_bytes(&public[..])?
|
||||
} else {
|
||||
(&secret).into()
|
||||
};
|
||||
Ok(ed25519_dalek::Keypair { secret, public })
|
||||
let secret = ed25519_dalek::SecretKey::try_from(&pair.secret_key[..]).map_err(|_| {
|
||||
Error::new(
|
||||
eyre!("pkcs8 key is of incorrect length"),
|
||||
ErrorKind::OpenSsl,
|
||||
)
|
||||
})?;
|
||||
Ok(secret.into())
|
||||
}
|
||||
}
|
||||
impl std::ops::Deref for SdkContext {
|
||||
|
||||
@@ -12,7 +12,7 @@ use crate::Error;
|
||||
#[command(display(display_none), metadata(sync_db = true))]
|
||||
#[instrument(skip_all)]
|
||||
pub async fn start(#[context] ctx: RpcContext, #[arg] id: PackageId) -> Result<(), Error> {
|
||||
let peek = ctx.db.peek().await?;
|
||||
let peek = ctx.db.peek().await;
|
||||
let version = peek
|
||||
.as_package_data()
|
||||
.as_idx(&id)
|
||||
@@ -27,14 +27,15 @@ pub async fn start(#[context] ctx: RpcContext, #[arg] id: PackageId) -> Result<(
|
||||
.get(&(id, version))
|
||||
.await
|
||||
.ok_or_else(|| Error::new(eyre!("Manager not found"), crate::ErrorKind::InvalidRequest))?
|
||||
.start();
|
||||
.start()
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[command(display(display_none), metadata(sync_db = true))]
|
||||
pub async fn stop(#[context] ctx: RpcContext, #[arg] id: PackageId) -> Result<MainStatus, Error> {
|
||||
let peek = ctx.db.peek().await?;
|
||||
let peek = ctx.db.peek().await;
|
||||
let version = peek
|
||||
.as_package_data()
|
||||
.as_idx(&id)
|
||||
@@ -62,14 +63,15 @@ pub async fn stop(#[context] ctx: RpcContext, #[arg] id: PackageId) -> Result<Ma
|
||||
.get(&(id, version))
|
||||
.await
|
||||
.ok_or_else(|| Error::new(eyre!("Manager not found"), crate::ErrorKind::InvalidRequest))?
|
||||
.stop();
|
||||
.stop()
|
||||
.await;
|
||||
|
||||
Ok(last_statuts)
|
||||
}
|
||||
|
||||
#[command(display(display_none), metadata(sync_db = true))]
|
||||
pub async fn restart(#[context] ctx: RpcContext, #[arg] id: PackageId) -> Result<(), Error> {
|
||||
let peek = ctx.db.peek().await?;
|
||||
let peek = ctx.db.peek().await;
|
||||
let version = peek
|
||||
.as_package_data()
|
||||
.as_idx(&id)
|
||||
@@ -83,7 +85,8 @@ pub async fn restart(#[context] ctx: RpcContext, #[arg] id: PackageId) -> Result
|
||||
.get(&(id, version))
|
||||
.await
|
||||
.ok_or_else(|| Error::new(eyre!("Manager not found"), crate::ErrorKind::InvalidRequest))?
|
||||
.restart();
|
||||
.restart()
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ async fn ws_handler<
|
||||
session: Option<(HasValidSession, HashSessionToken)>,
|
||||
ws_fut: WSFut,
|
||||
) -> Result<(), Error> {
|
||||
let (dump, sub) = ctx.db.dump_and_sub().await?;
|
||||
let (dump, sub) = ctx.db.dump_and_sub().await;
|
||||
let mut stream = ws_fut
|
||||
.await
|
||||
.with_kind(ErrorKind::Network)?
|
||||
@@ -82,6 +82,8 @@ async fn deal_with_messages(
|
||||
mut sub: patch_db::Subscriber,
|
||||
mut stream: WebSocketStream<Upgraded>,
|
||||
) -> Result<(), Error> {
|
||||
let mut timer = tokio::time::interval(tokio::time::Duration::from_secs(5));
|
||||
|
||||
loop {
|
||||
futures::select! {
|
||||
_ = (&mut kill).fuse() => {
|
||||
@@ -112,6 +114,13 @@ async fn deal_with_messages(
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
// This is trying to give a health checks to the home to keep the ui alive.
|
||||
_ = timer.tick().fuse() => {
|
||||
stream
|
||||
.send(Message::Ping(vec![]))
|
||||
.await
|
||||
.with_kind(crate::ErrorKind::Network)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -165,7 +174,7 @@ pub async fn subscribe(ctx: RpcContext, req: Request<Body>) -> Result<Response<B
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
#[command(subcommands(revisions, dump, put, apply))]
|
||||
#[command(subcommands(dump, put, apply))]
|
||||
pub fn db() -> Result<(), RpcError> {
|
||||
Ok(())
|
||||
}
|
||||
@@ -177,20 +186,6 @@ pub enum RevisionsRes {
|
||||
Dump(Dump),
|
||||
}
|
||||
|
||||
#[command(display(display_serializable))]
|
||||
pub async fn revisions(
|
||||
#[context] ctx: RpcContext,
|
||||
#[arg] since: u64,
|
||||
#[allow(unused_variables)]
|
||||
#[arg(long = "format")]
|
||||
format: Option<IoFormat>,
|
||||
) -> Result<RevisionsRes, Error> {
|
||||
Ok(match ctx.db.sync(since).await? {
|
||||
Ok(revs) => RevisionsRes::Revisions(revs),
|
||||
Err(dump) => RevisionsRes::Dump(dump),
|
||||
})
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
async fn cli_dump(
|
||||
ctx: CliContext,
|
||||
@@ -198,7 +193,7 @@ async fn cli_dump(
|
||||
path: Option<PathBuf>,
|
||||
) -> Result<Dump, RpcError> {
|
||||
let dump = if let Some(path) = path {
|
||||
PatchDb::open(path).await?.dump().await?
|
||||
PatchDb::open(path).await?.dump().await
|
||||
} else {
|
||||
rpc_toolkit::command_helpers::call_remote(
|
||||
ctx,
|
||||
@@ -226,7 +221,7 @@ pub async fn dump(
|
||||
#[arg]
|
||||
path: Option<PathBuf>,
|
||||
) -> Result<Dump, Error> {
|
||||
Ok(ctx.db.dump().await?)
|
||||
Ok(ctx.db.dump().await)
|
||||
}
|
||||
|
||||
fn apply_expr(input: jaq_core::Val, expr: &str) -> Result<jaq_core::Val, Error> {
|
||||
|
||||
@@ -47,7 +47,7 @@ impl Database {
|
||||
last_wifi_region: None,
|
||||
eos_version_compat: Current::new().compat().clone(),
|
||||
lan_address,
|
||||
tor_address: format!("http://{}", account.key.tor_address())
|
||||
tor_address: format!("https://{}", account.key.tor_address())
|
||||
.parse()
|
||||
.unwrap(),
|
||||
ip_info: BTreeMap::new(),
|
||||
@@ -426,7 +426,7 @@ pub struct InstalledPackageInfo {
|
||||
pub marketplace_url: Option<Url>,
|
||||
#[serde(default)]
|
||||
#[serde(with = "crate::util::serde::ed25519_pubkey")]
|
||||
pub developer_key: ed25519_dalek::PublicKey,
|
||||
pub developer_key: ed25519_dalek::VerifyingKey,
|
||||
pub manifest: Manifest,
|
||||
pub last_backup: Option<DateTime<Utc>>,
|
||||
pub dependency_info: BTreeMap<PackageId, StaticDependencyInfo>,
|
||||
@@ -483,6 +483,7 @@ pub struct StaticDependencyInfo {
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[model = "Model<Self>"]
|
||||
pub struct CurrentDependencyInfo {
|
||||
#[serde(default)]
|
||||
pub pointers: BTreeSet<PackagePointerSpec>,
|
||||
pub health_checks: BTreeSet<HealthCheckId>,
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ where
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait PatchDbExt {
|
||||
async fn peek(&self) -> Result<DatabaseModel, Error>;
|
||||
async fn peek(&self) -> DatabaseModel;
|
||||
async fn mutate<U: UnwindSafe + Send>(
|
||||
&self,
|
||||
f: impl FnOnce(&mut DatabaseModel) -> Result<U, Error> + UnwindSafe + Send,
|
||||
@@ -40,8 +40,8 @@ pub trait PatchDbExt {
|
||||
}
|
||||
#[async_trait::async_trait]
|
||||
impl PatchDbExt for PatchDb {
|
||||
async fn peek(&self) -> Result<DatabaseModel, Error> {
|
||||
Ok(DatabaseModel::from(self.dump().await?.value))
|
||||
async fn peek(&self) -> DatabaseModel {
|
||||
DatabaseModel::from(self.dump().await.value)
|
||||
}
|
||||
async fn mutate<U: UnwindSafe + Send>(
|
||||
&self,
|
||||
|
||||
@@ -170,7 +170,7 @@ pub async fn configure_logic(
|
||||
ctx: RpcContext,
|
||||
(pkg_id, dependency_id): (PackageId, PackageId),
|
||||
) -> Result<ConfigDryRes, Error> {
|
||||
let db = ctx.db.peek().await?;
|
||||
let db = ctx.db.peek().await;
|
||||
let pkg = db
|
||||
.as_package_data()
|
||||
.as_idx(&pkg_id)
|
||||
|
||||
@@ -3,7 +3,8 @@ use std::io::Write;
|
||||
use std::path::Path;
|
||||
|
||||
use ed25519::pkcs8::EncodePrivateKey;
|
||||
use ed25519_dalek::Keypair;
|
||||
use ed25519::PublicKeyBytes;
|
||||
use ed25519_dalek::{SigningKey, VerifyingKey};
|
||||
use rpc_toolkit::command;
|
||||
use tracing::instrument;
|
||||
|
||||
@@ -21,11 +22,11 @@ pub fn init(#[context] ctx: SdkContext) -> Result<(), Error> {
|
||||
.with_ctx(|_| (crate::ErrorKind::Filesystem, parent.display().to_string()))?;
|
||||
}
|
||||
tracing::info!("Generating new developer key...");
|
||||
let keypair = Keypair::generate(&mut rand_old::thread_rng());
|
||||
let secret = SigningKey::generate(&mut rand::thread_rng());
|
||||
tracing::info!("Writing key to {}", ctx.developer_key_path.display());
|
||||
let keypair_bytes = ed25519::KeypairBytes {
|
||||
secret_key: keypair.secret.to_bytes(),
|
||||
public_key: Some(keypair.public.to_bytes()),
|
||||
secret_key: secret.to_bytes(),
|
||||
public_key: Some(PublicKeyBytes(VerifyingKey::from(&secret).to_bytes())),
|
||||
};
|
||||
let mut dev_key_file = File::create(&ctx.developer_key_path)?;
|
||||
dev_key_file.write_all(
|
||||
|
||||
@@ -9,7 +9,6 @@ use crate::disk::repair;
|
||||
use crate::init::SYSTEM_REBUILD_PATH;
|
||||
use crate::logs::{fetch_logs, LogResponse, LogSource};
|
||||
use crate::shutdown::Shutdown;
|
||||
use crate::system::SYSTEMD_UNIT;
|
||||
use crate::util::display_none;
|
||||
use crate::Error;
|
||||
|
||||
@@ -29,7 +28,7 @@ pub async fn logs(
|
||||
#[arg] cursor: Option<String>,
|
||||
#[arg] before: bool,
|
||||
) -> Result<LogResponse, Error> {
|
||||
Ok(fetch_logs(LogSource::Service(SYSTEMD_UNIT), limit, cursor, before).await?)
|
||||
Ok(fetch_logs(LogSource::System, limit, cursor, before).await?)
|
||||
}
|
||||
|
||||
#[command(display(display_none))]
|
||||
@@ -42,8 +41,10 @@ pub fn exit(#[context] ctx: DiagnosticContext) -> Result<(), Error> {
|
||||
pub fn restart(#[context] ctx: DiagnosticContext) -> Result<(), Error> {
|
||||
ctx.shutdown
|
||||
.send(Some(Shutdown {
|
||||
datadir: ctx.datadir.clone(),
|
||||
disk_guid: ctx.disk_guid.clone(),
|
||||
export_args: ctx
|
||||
.disk_guid
|
||||
.clone()
|
||||
.map(|guid| (guid, ctx.datadir.clone())),
|
||||
restart: true,
|
||||
}))
|
||||
.expect("receiver dropped");
|
||||
|
||||
@@ -126,6 +126,7 @@ pub async fn create_fs<P: AsRef<Path>>(
|
||||
Command::new("cryptsetup")
|
||||
.arg("-q")
|
||||
.arg("luksOpen")
|
||||
.arg("--allow-discards")
|
||||
.arg(format!("--key-file={}", PASSWORD_PATH))
|
||||
.arg(format!("--keyfile-size={}", password.len()))
|
||||
.arg(&blockdev_path)
|
||||
|
||||
82
backend/src/firmware.rs
Normal file
82
backend/src/firmware.rs
Normal file
@@ -0,0 +1,82 @@
|
||||
use std::path::Path;
|
||||
use std::process::Stdio;
|
||||
|
||||
use async_compression::tokio::bufread::GzipDecoder;
|
||||
use tokio::fs::File;
|
||||
use tokio::io::{AsyncRead, AsyncWriteExt, BufReader};
|
||||
use tokio::process::Command;
|
||||
|
||||
use crate::disk::fsck::RequiresReboot;
|
||||
use crate::prelude::*;
|
||||
use crate::util::Invoke;
|
||||
|
||||
pub async fn update_firmware() -> Result<RequiresReboot, Error> {
|
||||
let product_name = String::from_utf8(
|
||||
Command::new("dmidecode")
|
||||
.arg("-s")
|
||||
.arg("system-product-name")
|
||||
.invoke(ErrorKind::Firmware)
|
||||
.await?,
|
||||
)?
|
||||
.trim()
|
||||
.to_owned();
|
||||
if product_name.is_empty() {
|
||||
return Ok(RequiresReboot(false));
|
||||
}
|
||||
let firmware_dir = Path::new("/usr/lib/embassy/firmware").join(&product_name);
|
||||
if tokio::fs::metadata(&firmware_dir).await.is_ok() {
|
||||
let current_firmware = String::from_utf8(
|
||||
Command::new("dmidecode")
|
||||
.arg("-s")
|
||||
.arg("bios-version")
|
||||
.invoke(ErrorKind::Firmware)
|
||||
.await?,
|
||||
)?
|
||||
.trim()
|
||||
.to_owned();
|
||||
if tokio::fs::metadata(firmware_dir.join(format!("{current_firmware}.rom.gz")))
|
||||
.await
|
||||
.is_err()
|
||||
&& tokio::fs::metadata(firmware_dir.join(format!("{current_firmware}.rom")))
|
||||
.await
|
||||
.is_err()
|
||||
{
|
||||
let mut firmware_read_dir = tokio::fs::read_dir(&firmware_dir).await?;
|
||||
while let Some(entry) = firmware_read_dir.next_entry().await? {
|
||||
let filename = entry.file_name().to_string_lossy().into_owned();
|
||||
let rdr: Option<Box<dyn AsyncRead + Unpin>> = if filename.ends_with(".rom.gz") {
|
||||
Some(Box::new(GzipDecoder::new(BufReader::new(
|
||||
File::open(entry.path()).await?,
|
||||
))))
|
||||
} else if filename.ends_with(".rom") {
|
||||
Some(Box::new(File::open(entry.path()).await?))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Some(mut rdr) = rdr {
|
||||
let mut flashrom = Command::new("flashrom")
|
||||
.arg("-p")
|
||||
.arg("internal")
|
||||
.arg("-w-")
|
||||
.stdin(Stdio::piped())
|
||||
.spawn()?;
|
||||
let mut rom_dest = flashrom.stdin.take().or_not_found("stdin")?;
|
||||
tokio::io::copy(&mut rdr, &mut rom_dest).await?;
|
||||
rom_dest.flush().await?;
|
||||
rom_dest.shutdown().await?;
|
||||
drop(rom_dest);
|
||||
let o = flashrom.wait_with_output().await?;
|
||||
if !o.status.success() {
|
||||
return Err(Error::new(
|
||||
eyre!("{}", std::str::from_utf8(&o.stderr)?),
|
||||
ErrorKind::Firmware,
|
||||
));
|
||||
} else {
|
||||
return Ok(RequiresReboot(true));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(RequiresReboot(false))
|
||||
}
|
||||
@@ -20,6 +20,9 @@ use crate::middleware::auth::LOCAL_AUTH_COOKIE_PATH;
|
||||
use crate::prelude::*;
|
||||
use crate::sound::BEP;
|
||||
use crate::system::time;
|
||||
use crate::util::cpupower::{
|
||||
current_governor, get_available_governors, set_governor, GOVERNOR_PERFORMANCE,
|
||||
};
|
||||
use crate::util::docker::{create_bridge_network, CONTAINER_DATADIR, CONTAINER_TOOL};
|
||||
use crate::util::Invoke;
|
||||
use crate::{Error, ARCH};
|
||||
@@ -200,13 +203,8 @@ pub async fn init(cfg: &RpcContextConfig) -> Result<InitResult, Error> {
|
||||
|
||||
let account = AccountInfo::load(&secret_store).await?;
|
||||
let db = cfg.db(&account).await?;
|
||||
db.mutate(|d| {
|
||||
let model = d.de()?;
|
||||
d.ser(&model)
|
||||
})
|
||||
.await?;
|
||||
tracing::info!("Opened PatchDB");
|
||||
let peek = db.peek().await?;
|
||||
let peek = db.peek().await;
|
||||
let mut server_info = peek.as_server_info().de()?;
|
||||
|
||||
// write to ca cert store
|
||||
@@ -268,6 +266,11 @@ pub async fn init(cfg: &RpcContextConfig) -> Result<InitResult, Error> {
|
||||
if tokio::fs::metadata(&tmp_dir).await.is_err() {
|
||||
tokio::fs::create_dir_all(&tmp_dir).await?;
|
||||
}
|
||||
let tmp_var = cfg.datadir().join(format!("package-data/tmp/var"));
|
||||
if tokio::fs::metadata(&tmp_var).await.is_ok() {
|
||||
tokio::fs::remove_dir_all(&tmp_var).await?;
|
||||
}
|
||||
crate::disk::mount::util::bind(&tmp_var, "/var/tmp", false).await?;
|
||||
let tmp_docker = cfg
|
||||
.datadir()
|
||||
.join(format!("package-data/tmp/{CONTAINER_TOOL}"));
|
||||
@@ -341,6 +344,23 @@ pub async fn init(cfg: &RpcContextConfig) -> Result<InitResult, Error> {
|
||||
.await?;
|
||||
tracing::info!("Enabled Docker QEMU Emulation");
|
||||
|
||||
if current_governor()
|
||||
.await?
|
||||
.map(|g| &g != &GOVERNOR_PERFORMANCE)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
tracing::info!("Setting CPU Governor to \"{}\"", GOVERNOR_PERFORMANCE);
|
||||
if get_available_governors()
|
||||
.await?
|
||||
.contains(&GOVERNOR_PERFORMANCE)
|
||||
{
|
||||
set_governor(&GOVERNOR_PERFORMANCE).await?;
|
||||
tracing::info!("Set CPU Governor");
|
||||
} else {
|
||||
tracing::warn!("CPU Governor \"{}\" Not Available", GOVERNOR_PERFORMANCE)
|
||||
}
|
||||
}
|
||||
|
||||
let mut warn_time_not_synced = true;
|
||||
for _ in 0..60 {
|
||||
if check_time_is_synchronized().await? {
|
||||
@@ -375,6 +395,12 @@ pub async fn init(cfg: &RpcContextConfig) -> Result<InitResult, Error> {
|
||||
|
||||
crate::version::init(&db, &secret_store).await?;
|
||||
|
||||
db.mutate(|d| {
|
||||
let model = d.de()?;
|
||||
d.ser(&model)
|
||||
})
|
||||
.await?;
|
||||
|
||||
if should_rebuild {
|
||||
match tokio::fs::remove_file(SYSTEM_REBUILD_PATH).await {
|
||||
Ok(()) => Ok(()),
|
||||
|
||||
@@ -62,7 +62,7 @@ pub async fn cleanup_failed(ctx: &RpcContext, id: &PackageId) -> Result<(), Erro
|
||||
if let Some(version) = match ctx
|
||||
.db
|
||||
.peek()
|
||||
.await?
|
||||
.await
|
||||
.as_package_data()
|
||||
.as_idx(id)
|
||||
.or_not_found(id)?
|
||||
@@ -141,7 +141,7 @@ pub async fn uninstall<Ex>(ctx: &RpcContext, secrets: &mut Ex, id: &PackageId) -
|
||||
where
|
||||
for<'a> &'a mut Ex: Executor<'a, Database = Postgres>,
|
||||
{
|
||||
let db = ctx.db.peek().await?;
|
||||
let db = ctx.db.peek().await;
|
||||
let entry = db
|
||||
.as_package_data()
|
||||
.as_idx(id)
|
||||
|
||||
@@ -22,7 +22,7 @@ use serde_json::{json, Value};
|
||||
use tokio::fs::{File, OpenOptions};
|
||||
use tokio::io::{AsyncRead, AsyncSeek, AsyncSeekExt, AsyncWriteExt};
|
||||
use tokio::process::Command;
|
||||
use tokio::sync::{oneshot, Mutex};
|
||||
use tokio::sync::oneshot;
|
||||
use tokio_stream::wrappers::ReadDirStream;
|
||||
use tracing::instrument;
|
||||
|
||||
@@ -64,7 +64,7 @@ pub const PKG_WASM_DIR: &str = "package-data/wasm";
|
||||
|
||||
#[command(display(display_serializable))]
|
||||
pub async fn list(#[context] ctx: RpcContext) -> Result<Value, Error> {
|
||||
Ok(ctx.db.peek().await?.as_package_data().as_entries()?
|
||||
Ok(ctx.db.peek().await.as_package_data().as_entries()?
|
||||
.iter()
|
||||
.filter_map(|(id, pde)| {
|
||||
let status = match pde.as_match() {
|
||||
@@ -626,9 +626,10 @@ pub async fn uninstall(
|
||||
let return_id = id.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) =
|
||||
async { cleanup::uninstall(&ctx, &mut ctx.secret_store.acquire().await?, &id).await }
|
||||
.await
|
||||
if let Err(e) = async {
|
||||
cleanup::uninstall(&ctx, ctx.secret_store.acquire().await?.as_mut(), &id).await
|
||||
}
|
||||
.await
|
||||
{
|
||||
let err_str = format!("Uninstall of {} Failed: {}", id, e);
|
||||
tracing::error!("{}", err_str);
|
||||
@@ -666,23 +667,11 @@ pub async fn download_install_s9pk(
|
||||
) -> Result<(), Error> {
|
||||
let pkg_id = &temp_manifest.id;
|
||||
let version = &temp_manifest.version;
|
||||
let previous_state: Arc<Mutex<Option<MainStatus>>> = Default::default();
|
||||
let db = ctx.db.peek().await?;
|
||||
let after_previous_state = previous_state.clone();
|
||||
let db = ctx.db.peek().await;
|
||||
|
||||
if let Result::<(), Error>::Err(e) = {
|
||||
let ctx = ctx.clone();
|
||||
async move {
|
||||
if db
|
||||
.as_package_data()
|
||||
.as_idx(&pkg_id)
|
||||
.or_not_found(&pkg_id)?
|
||||
.as_installed()
|
||||
.is_some()
|
||||
{
|
||||
*previous_state.lock().await =
|
||||
crate::control::stop(ctx.clone(), pkg_id.clone()).await.ok();
|
||||
}
|
||||
// // Build set of existing manifests
|
||||
let mut manifests = Vec::new();
|
||||
for (_id, pkg) in db.as_package_data().as_entries()? {
|
||||
@@ -699,7 +688,7 @@ pub async fn download_install_s9pk(
|
||||
for (p, lan) in cfg {
|
||||
if p.0 == 80 && lan.ssl || p.0 == 443 && !lan.ssl {
|
||||
return Err(Error::new(
|
||||
eyre!("SSL Conflict with embassyOS"),
|
||||
eyre!("SSL Conflict with StartOS"),
|
||||
ErrorKind::LanPortConflict,
|
||||
));
|
||||
}
|
||||
@@ -779,15 +768,6 @@ pub async fn download_install_s9pk(
|
||||
tracing::debug!("{:?}", e);
|
||||
}
|
||||
|
||||
let previous_state = after_previous_state.lock().await;
|
||||
if previous_state
|
||||
.as_ref()
|
||||
.map(|x| x.running())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
crate::control::start(ctx.clone(), pkg_id.clone()).await?;
|
||||
}
|
||||
|
||||
Err(e)
|
||||
} else {
|
||||
Ok::<_, Error>(())
|
||||
@@ -807,7 +787,7 @@ pub async fn install_s9pk<R: AsyncRead + AsyncSeek + Unpin + Send + Sync>(
|
||||
rdr.validated();
|
||||
let developer_key = rdr.developer_key().clone();
|
||||
rdr.reset().await?;
|
||||
let db = ctx.db.peek().await?;
|
||||
let db = ctx.db.peek().await;
|
||||
|
||||
tracing::info!("Install {}@{}: Unpacking Manifest", pkg_id, version);
|
||||
let manifest = progress
|
||||
@@ -845,7 +825,12 @@ pub async fn install_s9pk<R: AsyncRead + AsyncSeek + Unpin + Send + Sync>(
|
||||
.await
|
||||
.with_kind(crate::ErrorKind::Deserialization)?,
|
||||
)),
|
||||
Err(e) if e.status() == Some(StatusCode::BAD_REQUEST) => Ok(None),
|
||||
Err(e)
|
||||
if e.status() == Some(StatusCode::BAD_REQUEST)
|
||||
|| e.status() == Some(StatusCode::NOT_FOUND) =>
|
||||
{
|
||||
Ok(None)
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
.with_kind(crate::ErrorKind::Registry)?
|
||||
@@ -1033,6 +1018,12 @@ pub async fn install_s9pk<R: AsyncRead + AsyncSeek + Unpin + Send + Sync>(
|
||||
)
|
||||
.await?;
|
||||
|
||||
let peek = ctx.db.peek().await;
|
||||
let prev = peek
|
||||
.as_package_data()
|
||||
.as_idx(pkg_id)
|
||||
.or_not_found(pkg_id)?
|
||||
.de()?;
|
||||
let mut sql_tx = ctx.secret_store.begin().await?;
|
||||
|
||||
tracing::info!("Install {}@{}: Creating volumes", pkg_id, version);
|
||||
@@ -1040,7 +1031,7 @@ pub async fn install_s9pk<R: AsyncRead + AsyncSeek + Unpin + Send + Sync>(
|
||||
tracing::info!("Install {}@{}: Created volumes", pkg_id, version);
|
||||
|
||||
tracing::info!("Install {}@{}: Installing interfaces", pkg_id, version);
|
||||
let interface_addresses = manifest.interfaces.install(&mut sql_tx, pkg_id).await?;
|
||||
let interface_addresses = manifest.interfaces.install(sql_tx.as_mut(), pkg_id).await?;
|
||||
tracing::info!(
|
||||
"Install {}@{}: Installed interfaces {:?}",
|
||||
pkg_id,
|
||||
@@ -1095,17 +1086,10 @@ pub async fn install_s9pk<R: AsyncRead + AsyncSeek + Unpin + Send + Sync>(
|
||||
CurrentDependents(deps)
|
||||
};
|
||||
|
||||
let peek = ctx.db.peek().await?;
|
||||
let prev = peek
|
||||
.as_package_data()
|
||||
.as_idx(pkg_id)
|
||||
.or_not_found(pkg_id)?
|
||||
.de()?;
|
||||
let installed = InstalledPackageInfo {
|
||||
status: Status {
|
||||
configured: manifest.config.is_none(),
|
||||
main: MainStatus::Stopped,
|
||||
dependency_errors: Default::default(),
|
||||
dependency_config_errors: compute_dependency_config_errs(
|
||||
&ctx,
|
||||
&peek,
|
||||
@@ -1241,11 +1225,7 @@ pub async fn install_s9pk<R: AsyncRead + AsyncSeek + Unpin + Send + Sync>(
|
||||
manager.configure(configure_context).await?;
|
||||
}
|
||||
|
||||
if auto_start {
|
||||
manager.start();
|
||||
}
|
||||
|
||||
for to_configure in to_configure {
|
||||
for to_configure in to_configure.into_iter().filter(|(dep, _)| dep != pkg_id) {
|
||||
if let Err(e) = async {
|
||||
ctx.managers
|
||||
.get(&to_configure)
|
||||
@@ -1267,6 +1247,10 @@ pub async fn install_s9pk<R: AsyncRead + AsyncSeek + Unpin + Send + Sync>(
|
||||
}
|
||||
}
|
||||
|
||||
if auto_start {
|
||||
manager.start().await;
|
||||
}
|
||||
|
||||
tracing::info!("Install {}@{}: Complete", pkg_id, version);
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -28,6 +28,7 @@ pub mod developer;
|
||||
pub mod diagnostic;
|
||||
pub mod disk;
|
||||
pub mod error;
|
||||
pub mod firmware;
|
||||
pub mod hostname;
|
||||
pub mod init;
|
||||
pub mod inspect;
|
||||
|
||||
@@ -198,7 +198,7 @@ fn deserialize_string_or_utf8_array<'de, D: serde::de::Deserializer<'de>>(
|
||||
String::from_utf8(
|
||||
std::iter::repeat_with(|| seq.next_element::<u8>().transpose())
|
||||
.take_while(|a| a.is_some())
|
||||
.filter_map(|a| a)
|
||||
.flatten()
|
||||
.collect::<Result<Vec<u8>, _>>()?,
|
||||
)
|
||||
.map_err(serde::de::Error::custom)
|
||||
@@ -207,13 +207,22 @@ fn deserialize_string_or_utf8_array<'de, D: serde::de::Deserializer<'de>>(
|
||||
deserializer.deserialize_any(Visitor)
|
||||
}
|
||||
|
||||
/// Defining how we are going to filter on a journalctl cli log.
|
||||
/// Kernal: (-k --dmesg Show kernel message log from the current boot)
|
||||
/// Unit: ( -u --unit=UNIT Show logs from the specified unit
|
||||
/// --user-unit=UNIT Show logs from the specified user unit))
|
||||
/// System: Unit is startd, but we also filter on the comm
|
||||
/// Container: Filtering containers, like podman/docker is done by filtering on the CONTAINER_NAME
|
||||
#[derive(Debug)]
|
||||
pub enum LogSource {
|
||||
Kernel,
|
||||
Service(&'static str),
|
||||
Unit(&'static str),
|
||||
System,
|
||||
Container(PackageId),
|
||||
}
|
||||
|
||||
pub const SYSTEM_UNIT: &str = "startd";
|
||||
|
||||
#[command(
|
||||
custom_cli(cli_logs(async, context(CliContext))),
|
||||
subcommands(self(logs_nofollow(async)), logs_follow),
|
||||
@@ -323,21 +332,15 @@ pub async fn cli_logs_generic_follow(
|
||||
.into())
|
||||
}
|
||||
};
|
||||
base_url.set_scheme(ws_scheme).or_else(|_| {
|
||||
Err(Error::new(
|
||||
eyre!("Cannot set URL scheme"),
|
||||
crate::ErrorKind::ParseUrl,
|
||||
))
|
||||
})?;
|
||||
base_url
|
||||
.set_scheme(ws_scheme)
|
||||
.map_err(|_| Error::new(eyre!("Cannot set URL scheme"), crate::ErrorKind::ParseUrl))?;
|
||||
let (mut stream, _) =
|
||||
// base_url is "http://127.0.0.1/", with a trailing slash, so we don't put a leading slash in this path:
|
||||
tokio_tungstenite::connect_async(format!("{}ws/rpc/{}", base_url, res.guid)).await?;
|
||||
while let Some(log) = stream.try_next().await? {
|
||||
match log {
|
||||
Message::Text(log) => {
|
||||
println!("{}", serde_json::from_str::<LogEntry>(&log)?);
|
||||
}
|
||||
_ => (),
|
||||
if let Message::Text(log) = log {
|
||||
println!("{}", serde_json::from_str::<LogEntry>(&log)?);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,11 +364,22 @@ pub async fn journalctl(
|
||||
LogSource::Kernel => {
|
||||
cmd.arg("-k");
|
||||
}
|
||||
LogSource::Service(id) => {
|
||||
LogSource::Unit(id) => {
|
||||
cmd.arg("-u");
|
||||
cmd.arg(id);
|
||||
}
|
||||
LogSource::System => {
|
||||
cmd.arg("-u");
|
||||
cmd.arg(SYSTEM_UNIT);
|
||||
cmd.arg(format!("_COMM={}", SYSTEM_UNIT));
|
||||
}
|
||||
LogSource::Container(id) => {
|
||||
#[cfg(feature = "podman")]
|
||||
cmd.arg(format!(
|
||||
"SYSLOG_IDENTIFIER={}",
|
||||
DockerProcedure::container_name(&id, None)
|
||||
));
|
||||
#[cfg(not(feature = "podman"))]
|
||||
cmd.arg(format!(
|
||||
"CONTAINER_NAME={}",
|
||||
DockerProcedure::container_name(&id, None)
|
||||
@@ -373,7 +387,7 @@ pub async fn journalctl(
|
||||
}
|
||||
};
|
||||
|
||||
let cursor_formatted = format!("--after-cursor={}", cursor.clone().unwrap_or(""));
|
||||
let cursor_formatted = format!("--after-cursor={}", cursor.unwrap_or(""));
|
||||
if cursor.is_some() {
|
||||
cmd.arg(&cursor_formatted);
|
||||
if before {
|
||||
|
||||
@@ -11,7 +11,7 @@ use crate::Error;
|
||||
#[instrument(skip_all)]
|
||||
pub async fn check(ctx: &RpcContext, id: &PackageId) -> Result<(), Error> {
|
||||
let (manifest, started) = {
|
||||
let peeked = ctx.db.peek().await?;
|
||||
let peeked = ctx.db.peek().await;
|
||||
let pde = peeked
|
||||
.as_package_data()
|
||||
.as_idx(id)
|
||||
|
||||
@@ -53,7 +53,7 @@ impl ManageContainer {
|
||||
let current_state = Arc::new(watch::channel(StartStop::Stop).0);
|
||||
let desired_state = Arc::new(
|
||||
watch::channel::<StartStop>(
|
||||
get_status(seed.ctx.db.peek().await?, &seed.manifest).into(),
|
||||
get_status(seed.ctx.db.peek().await, &seed.manifest).into(),
|
||||
)
|
||||
.0,
|
||||
);
|
||||
@@ -103,7 +103,7 @@ impl ManageContainer {
|
||||
&self,
|
||||
seed: &manager_seed::ManagerSeed,
|
||||
) -> Result<(), Error> {
|
||||
let current_state = get_status(seed.ctx.db.peek().await?, &seed.manifest);
|
||||
let current_state = get_status(seed.ctx.db.peek().await, &seed.manifest);
|
||||
self.override_main_status
|
||||
.send_modify(|x| *x = Some(current_state));
|
||||
Ok(())
|
||||
|
||||
@@ -111,7 +111,7 @@ pub struct Manager {
|
||||
seed: Arc<ManagerSeed>,
|
||||
|
||||
manage_container: Arc<manager_container::ManageContainer>,
|
||||
transition: Arc<watch::Sender<Arc<TransitionState>>>,
|
||||
transition: Arc<watch::Sender<TransitionState>>,
|
||||
persistent_container: ManagerPersistentContainer,
|
||||
|
||||
pub gid: Arc<Gid>,
|
||||
@@ -140,60 +140,67 @@ impl Manager {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn start(&self) {
|
||||
self._transition_abort();
|
||||
self.manage_container.to_desired(StartStop::Start);
|
||||
}
|
||||
pub fn stop(&self) {
|
||||
self._transition_abort();
|
||||
self.manage_container.to_desired(StartStop::Stop);
|
||||
}
|
||||
pub fn restart(&self) {
|
||||
/// awaiting this does not wait for the start to complete
|
||||
pub async fn start(&self) {
|
||||
if self._is_transition_restart() {
|
||||
return;
|
||||
}
|
||||
self._transition_replace(self._transition_restart());
|
||||
self._transition_abort().await;
|
||||
self.manage_container.to_desired(StartStop::Start);
|
||||
}
|
||||
|
||||
/// awaiting this does not wait for the stop to complete
|
||||
pub async fn stop(&self) {
|
||||
self._transition_abort().await;
|
||||
self.manage_container.to_desired(StartStop::Stop);
|
||||
}
|
||||
/// awaiting this does not wait for the restart to complete
|
||||
pub async fn restart(&self) {
|
||||
if self._is_transition_restart()
|
||||
&& *self.manage_container.desired_state().borrow() == StartStop::Stop
|
||||
{
|
||||
return;
|
||||
}
|
||||
if self.manage_container.desired_state().borrow().is_start() {
|
||||
self._transition_replace(self._transition_restart()).await;
|
||||
}
|
||||
}
|
||||
/// awaiting this does not wait for the restart to complete
|
||||
pub async fn configure(
|
||||
&self,
|
||||
configure_context: ConfigureContext,
|
||||
) -> Result<BTreeMap<PackageId, String>, Error> {
|
||||
if self._is_transition_configure() {
|
||||
return Ok(configure_context.breakages);
|
||||
if self._is_transition_restart() {
|
||||
self._transition_abort().await;
|
||||
} else if self._is_transition_backup() {
|
||||
return Err(Error::new(
|
||||
eyre!("Can't configure because service is backing up"),
|
||||
ErrorKind::InvalidRequest,
|
||||
));
|
||||
}
|
||||
let context = self.seed.ctx.clone();
|
||||
let id = self.seed.manifest.id.clone();
|
||||
|
||||
let breakages = configure(context, id, configure_context).await?;
|
||||
self._transition_replace({
|
||||
let manage_container = self.manage_container.clone();
|
||||
let state_reverter = DesiredStateReverter::new(manage_container.clone());
|
||||
|
||||
let transition = self.transition.clone();
|
||||
TransitionState::Configuring(
|
||||
tokio::spawn(async move {
|
||||
manage_container.wait_for_desired(StartStop::Stop).await;
|
||||
self.restart().await;
|
||||
|
||||
state_reverter.revert().await;
|
||||
transition.send_replace(Default::default());
|
||||
})
|
||||
.into(),
|
||||
)
|
||||
});
|
||||
Ok(breakages)
|
||||
}
|
||||
|
||||
/// awaiting this does not wait for the backup to complete
|
||||
pub async fn backup(&self, backup_guard: BackupGuard) -> BackupReturn {
|
||||
if self._is_transition_backup() {
|
||||
return BackupReturn::AlreadyRunning(PackageBackupReport {
|
||||
error: Some("Can't do backup because service is in a backing up state".to_owned()),
|
||||
error: Some("Can't do backup because service is already backing up".to_owned()),
|
||||
});
|
||||
}
|
||||
let (transition_state, done) = self._transition_backup(backup_guard);
|
||||
self._transition_replace(transition_state);
|
||||
self._transition_replace(transition_state).await;
|
||||
done.await
|
||||
}
|
||||
pub async fn exit(&self) {
|
||||
self._transition_abort();
|
||||
self._transition_abort().await;
|
||||
self.manage_container
|
||||
.wait_for_desired(StartStop::Stop)
|
||||
.await;
|
||||
@@ -220,19 +227,14 @@ impl Manager {
|
||||
.map(|x| x.rpc_client())
|
||||
}
|
||||
|
||||
fn _transition_abort(&self) {
|
||||
if let Some(transition) = self
|
||||
.transition
|
||||
.send_replace(Default::default())
|
||||
.join_handle()
|
||||
{
|
||||
(**transition).abort();
|
||||
}
|
||||
}
|
||||
fn _transition_replace(&self, transition_state: TransitionState) {
|
||||
async fn _transition_abort(&self) {
|
||||
self.transition
|
||||
.send_replace(Arc::new(transition_state))
|
||||
.abort();
|
||||
.send_replace(Default::default())
|
||||
.abort()
|
||||
.await;
|
||||
}
|
||||
async fn _transition_replace(&self, transition_state: TransitionState) {
|
||||
self.transition.send_replace(transition_state).abort().await;
|
||||
}
|
||||
|
||||
pub(super) fn perform_restart(&self) -> impl Future<Output = Result<(), Error>> + 'static {
|
||||
@@ -265,7 +267,7 @@ impl Manager {
|
||||
let manage_container = self.manage_container.clone();
|
||||
let seed = self.seed.clone();
|
||||
async move {
|
||||
let peek = seed.ctx.db.peek().await?;
|
||||
let peek = seed.ctx.db.peek().await;
|
||||
let state_reverter = DesiredStateReverter::new(manage_container.clone());
|
||||
let override_guard =
|
||||
manage_container.set_override(get_status(peek, &seed.manifest).backing_up())?;
|
||||
@@ -322,15 +324,11 @@ impl Manager {
|
||||
}
|
||||
fn _is_transition_restart(&self) -> bool {
|
||||
let transition = self.transition.borrow();
|
||||
matches!(**transition, TransitionState::Restarting(_))
|
||||
matches!(*transition, TransitionState::Restarting(_))
|
||||
}
|
||||
fn _is_transition_backup(&self) -> bool {
|
||||
let transition = self.transition.borrow();
|
||||
matches!(**transition, TransitionState::BackingUp(_))
|
||||
}
|
||||
fn _is_transition_configure(&self) -> bool {
|
||||
let transition = self.transition.borrow();
|
||||
matches!(**transition, TransitionState::Configuring(_))
|
||||
matches!(*transition, TransitionState::BackingUp(_))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -340,7 +338,7 @@ async fn configure(
|
||||
id: PackageId,
|
||||
mut configure_context: ConfigureContext,
|
||||
) -> Result<BTreeMap<PackageId, String>, Error> {
|
||||
let db = ctx.db.peek().await?;
|
||||
let db = ctx.db.peek().await;
|
||||
let id = &id;
|
||||
let ctx = &ctx;
|
||||
let overrides = &mut configure_context.overrides;
|
||||
@@ -602,7 +600,7 @@ impl Drop for DesiredStateReverter {
|
||||
type BackupDoneSender = oneshot::Sender<Result<PackageBackupInfo, Error>>;
|
||||
|
||||
fn finish_up_backup_task(
|
||||
transition: Arc<Sender<Arc<TransitionState>>>,
|
||||
transition: Arc<Sender<TransitionState>>,
|
||||
send: BackupDoneSender,
|
||||
) -> impl FnOnce(Result<Result<PackageBackupInfo, Error>, Error>) -> BoxFuture<'static, ()> {
|
||||
move |result| {
|
||||
@@ -761,7 +759,7 @@ async fn add_network_for_main(
|
||||
for (id, interface) in &seed.manifest.interfaces.0 {
|
||||
for (external, internal) in interface.lan_config.iter().flatten() {
|
||||
svc.add_lan(
|
||||
&mut tx,
|
||||
tx.as_mut(),
|
||||
id.clone(),
|
||||
external.0,
|
||||
internal.internal,
|
||||
@@ -770,13 +768,14 @@ async fn add_network_for_main(
|
||||
.await?;
|
||||
}
|
||||
for (external, internal) in interface.tor_config.iter().flat_map(|t| &t.port_mapping) {
|
||||
svc.add_tor(&mut tx, id.clone(), external.0, internal.0)
|
||||
svc.add_tor(tx.as_mut(), id.clone(), external.0, internal.0)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
for volume in seed.manifest.volumes.values() {
|
||||
if let Volume::Certificate { interface_id } = volume {
|
||||
svc.export_cert(&mut tx, interface_id, ip.into()).await?;
|
||||
svc.export_cert(tx.as_mut(), interface_id, ip.into())
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
tx.commit().await?;
|
||||
|
||||
@@ -5,21 +5,26 @@ use helpers::NonDetachingJoinHandle;
|
||||
pub(super) enum TransitionState {
|
||||
BackingUp(NonDetachingJoinHandle<()>),
|
||||
Restarting(NonDetachingJoinHandle<()>),
|
||||
Configuring(NonDetachingJoinHandle<()>),
|
||||
None,
|
||||
}
|
||||
|
||||
impl TransitionState {
|
||||
pub(super) fn join_handle(&self) -> Option<&NonDetachingJoinHandle<()>> {
|
||||
pub(super) fn take(&mut self) -> Self {
|
||||
std::mem::take(self)
|
||||
}
|
||||
pub(super) fn into_join_handle(self) -> Option<NonDetachingJoinHandle<()>> {
|
||||
Some(match self {
|
||||
TransitionState::BackingUp(a) => a,
|
||||
TransitionState::Restarting(a) => a,
|
||||
TransitionState::Configuring(a) => a,
|
||||
TransitionState::None => return None,
|
||||
})
|
||||
}
|
||||
pub(super) fn abort(&self) {
|
||||
self.join_handle().map(|transition| transition.abort());
|
||||
pub(super) async fn abort(&mut self) {
|
||||
if let Some(s) = self.take().into_join_handle() {
|
||||
if s.wait_for_abort().await.is_ok() {
|
||||
tracing::trace!("transition completed before abort");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ impl HasLoggedOutSessions {
|
||||
"UPDATE session SET logged_out = CURRENT_TIMESTAMP WHERE id = $1",
|
||||
session
|
||||
)
|
||||
.execute(&mut sqlx_conn)
|
||||
.execute(sqlx_conn.as_mut())
|
||||
.await?;
|
||||
for socket in open_authed_websockets.remove(&session).unwrap_or_default() {
|
||||
let _ = socket.send(());
|
||||
@@ -94,7 +94,7 @@ impl HasValidSession {
|
||||
pub async fn from_session(session: &HashSessionToken, ctx: &RpcContext) -> Result<Self, Error> {
|
||||
let session_hash = session.hashed();
|
||||
let session = sqlx::query!("UPDATE session SET last_active = CURRENT_TIMESTAMP WHERE id = $1 AND logged_out IS NULL OR logged_out > CURRENT_TIMESTAMP", session_hash)
|
||||
.execute(&mut ctx.secret_store.acquire().await?)
|
||||
.execute(ctx.secret_store.acquire().await?.as_mut())
|
||||
.await?;
|
||||
if session.rows_affected() == 0 {
|
||||
return Err(Error::new(
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
use futures::FutureExt;
|
||||
use http::HeaderValue;
|
||||
use hyper::header::HeaderMap;
|
||||
use rpc_toolkit::hyper::http::Error as HttpError;
|
||||
use rpc_toolkit::hyper::{Body, Method, Request, Response};
|
||||
use rpc_toolkit::rpc_server_helpers::{
|
||||
@@ -6,24 +8,35 @@ use rpc_toolkit::rpc_server_helpers::{
|
||||
};
|
||||
use rpc_toolkit::Metadata;
|
||||
|
||||
fn get_cors_headers(req: &Request<Body>) -> HeaderMap {
|
||||
let mut res = HeaderMap::new();
|
||||
if let Some(origin) = req.headers().get("Origin") {
|
||||
res.insert("Access-Control-Allow-Origin", origin.clone());
|
||||
}
|
||||
if let Some(method) = req.headers().get("Access-Control-Request-Method") {
|
||||
res.insert("Access-Control-Allow-Methods", method.clone());
|
||||
}
|
||||
if let Some(headers) = req.headers().get("Access-Control-Request-Headers") {
|
||||
res.insert("Access-Control-Allow-Headers", headers.clone());
|
||||
}
|
||||
res.insert(
|
||||
"Access-Control-Allow-Credentials",
|
||||
HeaderValue::from_static("true"),
|
||||
);
|
||||
res
|
||||
}
|
||||
|
||||
pub async fn cors<M: Metadata>(
|
||||
req: &mut Request<Body>,
|
||||
_metadata: M,
|
||||
) -> Result<Result<DynMiddlewareStage2, Response<Body>>, HttpError> {
|
||||
let headers = get_cors_headers(req);
|
||||
if req.method() == Method::OPTIONS {
|
||||
Ok(Err(Response::builder()
|
||||
.header(
|
||||
"Access-Control-Allow-Origin",
|
||||
if let Some(origin) = req.headers().get("origin").and_then(|s| s.to_str().ok()) {
|
||||
origin
|
||||
} else {
|
||||
"*"
|
||||
},
|
||||
)
|
||||
.header("Access-Control-Allow-Methods", "*")
|
||||
.header("Access-Control-Allow-Headers", "*")
|
||||
.header("Access-Control-Allow-Credentials", "true")
|
||||
.body(Body::empty())?))
|
||||
Ok(Err({
|
||||
let mut res = Response::new(Body::empty());
|
||||
res.headers_mut().extend(headers.into_iter());
|
||||
res
|
||||
}))
|
||||
} else {
|
||||
Ok(Ok(Box::new(|_, _| {
|
||||
async move {
|
||||
@@ -31,8 +44,7 @@ pub async fn cors<M: Metadata>(
|
||||
async move {
|
||||
let res: DynMiddlewareStage4 = Box::new(|res| {
|
||||
async move {
|
||||
res.headers_mut()
|
||||
.insert("Access-Control-Allow-Origin", "*".parse()?);
|
||||
res.headers_mut().extend(headers.into_iter());
|
||||
Ok::<_, HttpError>(())
|
||||
}
|
||||
.boxed()
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use color_eyre::eyre::eyre;
|
||||
use futures::future::BoxFuture;
|
||||
use futures::FutureExt;
|
||||
use http::HeaderValue;
|
||||
@@ -11,7 +10,6 @@ use rpc_toolkit::yajrc::RpcMethod;
|
||||
use rpc_toolkit::Metadata;
|
||||
|
||||
use crate::context::RpcContext;
|
||||
use crate::{Error, ResultExt};
|
||||
|
||||
pub fn db<M: Metadata>(ctx: RpcContext) -> DynMiddleware<M> {
|
||||
Box::new(
|
||||
@@ -20,51 +18,21 @@ pub fn db<M: Metadata>(ctx: RpcContext) -> DynMiddleware<M> {
|
||||
-> BoxFuture<Result<Result<DynMiddlewareStage2, Response<Body>>, HttpError>> {
|
||||
let ctx = ctx.clone();
|
||||
async move {
|
||||
let m2: DynMiddlewareStage2 = Box::new(move |req, rpc_req| {
|
||||
let m2: DynMiddlewareStage2 = Box::new(move |_req, rpc_req| {
|
||||
async move {
|
||||
let seq = req.headers.remove("x-patch-sequence");
|
||||
let sync_db = metadata
|
||||
.get(rpc_req.method.as_str(), "sync_db")
|
||||
.unwrap_or(false);
|
||||
|
||||
let m3: DynMiddlewareStage3 = Box::new(move |res, _| {
|
||||
async move {
|
||||
if sync_db && seq.is_some() {
|
||||
match async {
|
||||
let seq = seq
|
||||
.ok_or_else(|| {
|
||||
Error::new(
|
||||
eyre!("Missing X-Patch-Sequence"),
|
||||
crate::ErrorKind::InvalidRequest,
|
||||
)
|
||||
})?
|
||||
.to_str()
|
||||
.with_kind(crate::ErrorKind::InvalidRequest)?
|
||||
.parse()?;
|
||||
let res = ctx.db.sync(seq).await?;
|
||||
let json = match res {
|
||||
Ok(revs) => serde_json::to_vec(&revs),
|
||||
Err(dump) => serde_json::to_vec(&[dump]),
|
||||
}
|
||||
.with_kind(crate::ErrorKind::Serialization)?;
|
||||
Ok::<_, Error>(base64::encode_config(
|
||||
&json,
|
||||
base64::URL_SAFE,
|
||||
))
|
||||
}
|
||||
.await
|
||||
{
|
||||
Ok(a) => res
|
||||
.headers
|
||||
.append("X-Patch-Updates", HeaderValue::from_str(&a)?),
|
||||
Err(e) => res.headers.append(
|
||||
"X-Patch-Error",
|
||||
HeaderValue::from_str(&base64::encode_config(
|
||||
&e.to_string(),
|
||||
base64::URL_SAFE,
|
||||
))?,
|
||||
),
|
||||
};
|
||||
if sync_db {
|
||||
res.headers.append(
|
||||
"X-Patch-Sequence",
|
||||
HeaderValue::from_str(
|
||||
&ctx.db.sequence().await.to_string(),
|
||||
)?,
|
||||
);
|
||||
}
|
||||
Ok(Ok(noop4()))
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ pub fn pbkdf2(password: impl AsRef<[u8]>, salt: impl AsRef<[u8]>) -> CipherKey<A
|
||||
salt.as_ref(),
|
||||
1000,
|
||||
aeskey.as_mut_slice(),
|
||||
);
|
||||
).unwrap();
|
||||
aeskey
|
||||
}
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@ use tokio::process::Command;
|
||||
use tokio::sync::RwLock;
|
||||
use tracing::instrument;
|
||||
use trust_dns_server::authority::MessageResponseBuilder;
|
||||
use trust_dns_server::client::op::{Header, ResponseCode};
|
||||
use trust_dns_server::client::rr::{Name, Record, RecordType};
|
||||
use trust_dns_server::proto::op::{Header, ResponseCode};
|
||||
use trust_dns_server::proto::rr::{Name, Record, RecordType};
|
||||
use trust_dns_server::server::{Request, RequestHandler, ResponseHandler, ResponseInfo};
|
||||
use trust_dns_server::ServerFuture;
|
||||
|
||||
@@ -86,7 +86,7 @@ impl RequestHandler for Resolver {
|
||||
Record::from_rdata(
|
||||
request.request_info().query.name().to_owned().into(),
|
||||
0,
|
||||
trust_dns_server::client::rr::RData::A(ip),
|
||||
trust_dns_server::proto::rr::RData::A(ip.into()),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use color_eyre::eyre::eyre;
|
||||
use ed25519_dalek::{ExpandedSecretKey, SecretKey};
|
||||
use models::{Id, InterfaceId, PackageId};
|
||||
use openssl::pkey::{PKey, Private};
|
||||
use openssl::sha::Sha256;
|
||||
@@ -12,14 +11,15 @@ use tracing::instrument;
|
||||
use zeroize::Zeroize;
|
||||
|
||||
use crate::net::ssl::CertPair;
|
||||
use crate::Error;
|
||||
use crate::prelude::*;
|
||||
use crate::util::crypto::ed25519_expand_key;
|
||||
|
||||
// TODO: delete once we may change tor addresses
|
||||
#[instrument(skip(secrets))]
|
||||
async fn compat(
|
||||
secrets: impl PgExecutor<'_>,
|
||||
interface: &Option<(PackageId, InterfaceId)>,
|
||||
) -> Result<Option<ExpandedSecretKey>, Error> {
|
||||
) -> Result<Option<[u8; 64]>, Error> {
|
||||
if let Some((package, interface)) = interface {
|
||||
if let Some(r) = sqlx::query!(
|
||||
"SELECT key FROM tor WHERE package = $1 AND interface = $2",
|
||||
@@ -29,7 +29,12 @@ async fn compat(
|
||||
.fetch_optional(secrets)
|
||||
.await?
|
||||
{
|
||||
Ok(Some(ExpandedSecretKey::from_bytes(&r.key)?))
|
||||
Ok(Some(<[u8; 64]>::try_from(r.key).map_err(|e| {
|
||||
Error::new(
|
||||
eyre!("expected vec of len 64, got len {}", e.len()),
|
||||
ErrorKind::ParseDbField,
|
||||
)
|
||||
})?))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
@@ -38,7 +43,12 @@ async fn compat(
|
||||
.await?
|
||||
.tor_key
|
||||
{
|
||||
Ok(Some(ExpandedSecretKey::from_bytes(&key)?))
|
||||
Ok(Some(<[u8; 64]>::try_from(key).map_err(|e| {
|
||||
Error::new(
|
||||
eyre!("expected vec of len 64, got len {}", e.len()),
|
||||
ErrorKind::ParseDbField,
|
||||
)
|
||||
})?))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
@@ -64,10 +74,7 @@ impl Key {
|
||||
.unwrap_or_else(|| "embassy".to_owned())
|
||||
}
|
||||
pub fn tor_key(&self) -> TorSecretKeyV3 {
|
||||
ed25519_dalek::ExpandedSecretKey::from_bytes(&self.tor_key)
|
||||
.unwrap()
|
||||
.to_bytes()
|
||||
.into()
|
||||
self.tor_key.into()
|
||||
}
|
||||
pub fn tor_address(&self) -> OnionAddressV3 {
|
||||
self.tor_key().public().get_onion_address()
|
||||
@@ -87,7 +94,7 @@ impl Key {
|
||||
pub fn openssl_key_nistp256(&self) -> PKey<Private> {
|
||||
let mut buf = self.base;
|
||||
loop {
|
||||
if let Ok(k) = p256::SecretKey::from_be_bytes(&buf) {
|
||||
if let Ok(k) = p256::SecretKey::from_slice(&buf) {
|
||||
return PKey::private_key_from_pkcs8(&*k.to_pkcs8_der().unwrap().as_bytes())
|
||||
.unwrap();
|
||||
}
|
||||
@@ -111,11 +118,7 @@ impl Key {
|
||||
}
|
||||
}
|
||||
pub fn from_bytes(interface: Option<(PackageId, InterfaceId)>, bytes: [u8; 32]) -> Self {
|
||||
Self::from_pair(
|
||||
interface,
|
||||
bytes,
|
||||
ExpandedSecretKey::from(&SecretKey::from_bytes(&bytes).unwrap()).to_bytes(),
|
||||
)
|
||||
Self::from_pair(interface, bytes, ed25519_expand_key(&bytes))
|
||||
}
|
||||
pub fn new(interface: Option<(PackageId, InterfaceId)>) -> Self {
|
||||
Self::from_bytes(interface, rand::random())
|
||||
@@ -225,7 +228,7 @@ impl Key {
|
||||
};
|
||||
let mut res = Self::from_bytes(interface, actual);
|
||||
if let Some(tor_key) = compat(secrets, &res.interface).await? {
|
||||
res.tor_key = tor_key.to_bytes();
|
||||
res.tor_key = tor_key;
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ pub mod wifi;
|
||||
|
||||
pub const PACKAGE_CERT_PATH: &str = "/var/lib/embassy/ssl";
|
||||
|
||||
#[command(subcommands(tor::tor, dhcp::dhcp))]
|
||||
#[command(subcommands(tor::tor, dhcp::dhcp, ssl::ssl))]
|
||||
pub fn net() -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -159,6 +159,7 @@ impl NetController {
|
||||
let dns = self.dns.add(Some(package.clone()), ip).await?;
|
||||
|
||||
Ok(NetService {
|
||||
shutdown: false,
|
||||
id: package,
|
||||
ip,
|
||||
dns,
|
||||
@@ -225,6 +226,7 @@ impl NetController {
|
||||
}
|
||||
|
||||
pub struct NetService {
|
||||
shutdown: bool,
|
||||
id: PackageId,
|
||||
ip: Ipv4Addr,
|
||||
dns: Arc<()>,
|
||||
@@ -372,6 +374,7 @@ impl NetService {
|
||||
Ok(())
|
||||
}
|
||||
pub async fn remove_all(mut self) -> Result<(), Error> {
|
||||
self.shutdown = true;
|
||||
let mut errors = ErrorCollection::new();
|
||||
if let Some(ctrl) = Weak::upgrade(&self.controller) {
|
||||
for ((_, external), (key, rcs)) in std::mem::take(&mut self.lan) {
|
||||
@@ -385,9 +388,9 @@ impl NetService {
|
||||
}
|
||||
std::mem::take(&mut self.dns);
|
||||
errors.handle(ctrl.dns.gc(Some(self.id.clone()), self.ip).await);
|
||||
self.ip = Ipv4Addr::new(0, 0, 0, 0);
|
||||
errors.into_result()
|
||||
} else {
|
||||
tracing::warn!("NetService dropped after NetController is shutdown");
|
||||
Err(Error::new(
|
||||
eyre!("NetController is shutdown"),
|
||||
crate::ErrorKind::Network,
|
||||
@@ -398,11 +401,12 @@ impl NetService {
|
||||
|
||||
impl Drop for NetService {
|
||||
fn drop(&mut self) {
|
||||
if self.ip != Ipv4Addr::new(0, 0, 0, 0) {
|
||||
if !self.shutdown {
|
||||
tracing::debug!("Dropping NetService for {}", self.id);
|
||||
let svc = std::mem::replace(
|
||||
self,
|
||||
NetService {
|
||||
shutdown: true,
|
||||
id: Default::default(),
|
||||
ip: Ipv4Addr::new(0, 0, 0, 0),
|
||||
dns: Default::default(),
|
||||
|
||||
@@ -13,14 +13,15 @@ use openssl::nid::Nid;
|
||||
use openssl::pkey::{PKey, Private};
|
||||
use openssl::x509::{X509Builder, X509Extension, X509NameBuilder, X509};
|
||||
use openssl::*;
|
||||
use rpc_toolkit::command;
|
||||
use tokio::sync::{Mutex, RwLock};
|
||||
use tracing::instrument;
|
||||
|
||||
use crate::account::AccountInfo;
|
||||
use crate::context::RpcContext;
|
||||
use crate::hostname::Hostname;
|
||||
use crate::net::dhcp::ips;
|
||||
use crate::net::keys::{Key, KeyInfo};
|
||||
use crate::s9pk::manifest::PackageId;
|
||||
use crate::{Error, ErrorKind, ResultExt};
|
||||
|
||||
static CERTIFICATE_VERSION: i32 = 2; // X509 version 3 is actually encoded as '2' in the cert because fuck you.
|
||||
@@ -415,3 +416,16 @@ pub fn make_leaf_cert(
|
||||
let cert = builder.build();
|
||||
Ok(cert)
|
||||
}
|
||||
|
||||
#[command(subcommands(size))]
|
||||
pub async fn ssl() -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub async fn size(#[context] ctx: RpcContext) -> Result<String, Error> {
|
||||
Ok(format!(
|
||||
"Cert Catch size: {}",
|
||||
ctx.net_controller.ssl.cert_cache.read().await.len()
|
||||
))
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ pub async fn logs_nofollow(
|
||||
_ctx: (),
|
||||
(limit, cursor, before, _): (Option<usize>, Option<String>, bool, bool),
|
||||
) -> Result<LogResponse, Error> {
|
||||
fetch_logs(LogSource::Service(SYSTEMD_UNIT), limit, cursor, before).await
|
||||
fetch_logs(LogSource::Unit(SYSTEMD_UNIT), limit, cursor, before).await
|
||||
}
|
||||
|
||||
#[command(rpc_only, rename = "follow", display(display_none))]
|
||||
@@ -147,7 +147,7 @@ pub async fn logs_follow(
|
||||
#[context] ctx: RpcContext,
|
||||
#[parent_data] (limit, _, _, _): (Option<usize>, Option<String>, bool, bool),
|
||||
) -> Result<LogFollowResponse, Error> {
|
||||
follow_logs(ctx, LogSource::Service(SYSTEMD_UNIT), limit).await
|
||||
follow_logs(ctx, LogSource::Unit(SYSTEMD_UNIT), limit).await
|
||||
}
|
||||
|
||||
fn event_handler(_event: AsyncEvent<'static>) -> BoxFuture<'static, Result<(), ConnError>> {
|
||||
@@ -305,7 +305,7 @@ async fn torctl(
|
||||
.invoke(ErrorKind::Tor)
|
||||
.await?;
|
||||
|
||||
let logs = journalctl(LogSource::Service(SYSTEMD_UNIT), 0, None, false, true).await?;
|
||||
let logs = journalctl(LogSource::Unit(SYSTEMD_UNIT), 0, None, false, true).await?;
|
||||
|
||||
let mut tcp_stream = None;
|
||||
for _ in 0..60 {
|
||||
|
||||
@@ -16,12 +16,13 @@ use tokio::sync::{Mutex, RwLock};
|
||||
use tokio_rustls::rustls::server::Acceptor;
|
||||
use tokio_rustls::rustls::{RootCertStore, ServerConfig};
|
||||
use tokio_rustls::{LazyConfigAcceptor, TlsConnector};
|
||||
use tracing::instrument;
|
||||
|
||||
use crate::net::keys::Key;
|
||||
use crate::net::ssl::SslManager;
|
||||
use crate::net::utils::SingleAccept;
|
||||
use crate::prelude::*;
|
||||
use crate::util::io::{BackTrackingReader, TimeoutStream};
|
||||
use crate::Error;
|
||||
|
||||
// not allowed: <=1024, >=32768, 5355, 5432, 9050, 6010, 9051, 5353
|
||||
|
||||
@@ -36,6 +37,7 @@ impl VHostController {
|
||||
servers: Mutex::new(BTreeMap::new()),
|
||||
}
|
||||
}
|
||||
#[instrument(skip_all)]
|
||||
pub async fn add(
|
||||
&self,
|
||||
key: Key,
|
||||
@@ -63,6 +65,7 @@ impl VHostController {
|
||||
writable.insert(external, server);
|
||||
Ok(rc?)
|
||||
}
|
||||
#[instrument(skip_all)]
|
||||
pub async fn gc(&self, hostname: Option<String>, external: u16) -> Result<(), Error> {
|
||||
let mut writable = self.servers.lock().await;
|
||||
if let Some(server) = writable.remove(&external) {
|
||||
@@ -93,6 +96,7 @@ struct VHostServer {
|
||||
_thread: NonDetachingJoinHandle<()>,
|
||||
}
|
||||
impl VHostServer {
|
||||
#[instrument(skip_all)]
|
||||
async fn new(port: u16, ssl: Arc<SslManager>) -> Result<Self, Error> {
|
||||
// check if port allowed
|
||||
let listener = TcpListener::bind(SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), port))
|
||||
|
||||
@@ -174,7 +174,7 @@ pub async fn delete(#[context] ctx: RpcContext, #[arg] ssid: String) -> Result<(
|
||||
pub struct WiFiInfo {
|
||||
ssids: HashMap<Ssid, SignalStrength>,
|
||||
connected: Option<Ssid>,
|
||||
country: CountryCode,
|
||||
country: Option<CountryCode>,
|
||||
ethernet: bool,
|
||||
available_wifi: Vec<WifiListOut>,
|
||||
}
|
||||
@@ -216,7 +216,7 @@ fn display_wifi_info(info: WiFiInfo, matches: &ArgMatches) {
|
||||
.as_ref()
|
||||
.and_then(|x| info.ssids.get(x))
|
||||
.map_or("[N/A]".to_owned(), |ss| format!("{}", ss.0)),
|
||||
&info.country.alpha2(),
|
||||
info.country.as_ref().map(|c| c.alpha2()).unwrap_or("00"),
|
||||
&format!("{}", info.ethernet)
|
||||
]);
|
||||
table_global.print_tty(false).unwrap();
|
||||
@@ -517,7 +517,7 @@ impl WpaCli {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
pub async fn get_country_low(&self) -> Result<CountryCode, Error> {
|
||||
pub async fn get_country_low(&self) -> Result<Option<CountryCode>, Error> {
|
||||
let r = Command::new("iw")
|
||||
.arg("reg")
|
||||
.arg("get")
|
||||
@@ -539,12 +539,16 @@ impl WpaCli {
|
||||
ErrorKind::Wifi,
|
||||
)
|
||||
})?[1];
|
||||
Ok(CountryCode::for_alpha2(country).map_err(|_| {
|
||||
Error::new(
|
||||
color_eyre::eyre::eyre!("Invalid Country Code: {}", country),
|
||||
ErrorKind::Wifi,
|
||||
)
|
||||
})?)
|
||||
if country == "00" {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(CountryCode::for_alpha2(country).map_err(|_| {
|
||||
Error::new(
|
||||
color_eyre::eyre::eyre!("Invalid Country Code: {}", country),
|
||||
ErrorKind::Wifi,
|
||||
)
|
||||
})?))
|
||||
}
|
||||
}
|
||||
pub async fn remove_network_low(&mut self, id: NetworkId) -> Result<(), Error> {
|
||||
let _ = Command::new("nmcli")
|
||||
@@ -634,7 +638,7 @@ impl WpaCli {
|
||||
Ok(())
|
||||
}
|
||||
pub async fn save_config(&mut self, db: PatchDb) -> Result<(), Error> {
|
||||
let new_country = Some(self.get_country_low().await?);
|
||||
let new_country = self.get_country_low().await?;
|
||||
db.mutate(|d| {
|
||||
d.as_server_info_mut()
|
||||
.as_last_wifi_region_mut()
|
||||
|
||||
@@ -236,7 +236,7 @@ impl NotificationManager {
|
||||
subtype: T,
|
||||
debounce_interval: Option<u32>,
|
||||
) -> Result<(), Error> {
|
||||
let peek = db.peek().await?;
|
||||
let peek = db.peek().await;
|
||||
if !self
|
||||
.should_notify(&package_id, &level, &title, debounce_interval)
|
||||
.await
|
||||
|
||||
@@ -1,24 +1,27 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use std::{
|
||||
path::{Path, PathBuf},
|
||||
process::Stdio,
|
||||
};
|
||||
|
||||
use color_eyre::eyre::eyre;
|
||||
use color_eyre::Report;
|
||||
use embassy_container_init::{ProcessGroupId, SignalGroup, SignalGroupParams};
|
||||
use helpers::{Address, AddressSchemaLocal, AddressSchemaOnion, Callback, OsApi, UnixRpcClient};
|
||||
use embassy_container_init::ProcessGroupId;
|
||||
use helpers::UnixRpcClient;
|
||||
pub use js_engine::JsError;
|
||||
use js_engine::{JsExecutionEnvironment, PathForVolumeId};
|
||||
use models::{ErrorKind, InterfaceId, VolumeId};
|
||||
use models::VolumeId;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::process::Command;
|
||||
use tracing::instrument;
|
||||
|
||||
use super::ProcedureName;
|
||||
use crate::context::RpcContext;
|
||||
use crate::prelude::*;
|
||||
use crate::s9pk::manifest::PackageId;
|
||||
use crate::util::{GeneralGuard, Version};
|
||||
use crate::util::io::to_json_async_writer;
|
||||
use crate::util::Version;
|
||||
use crate::volume::Volumes;
|
||||
use crate::Error;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
@@ -46,56 +49,15 @@ impl PathForVolumeId for Volumes {
|
||||
}
|
||||
}
|
||||
|
||||
struct SandboxOsApi {
|
||||
_ctx: RpcContext,
|
||||
}
|
||||
#[async_trait::async_trait]
|
||||
impl OsApi for SandboxOsApi {
|
||||
#[allow(unused_variables)]
|
||||
async fn get_service_config(
|
||||
&self,
|
||||
id: PackageId,
|
||||
path: &str,
|
||||
callback: Option<Callback>,
|
||||
) -> Result<Vec<serde_json::Value>, Report> {
|
||||
Err(eyre!("Operation not permitted"))
|
||||
}
|
||||
#[allow(unused_variables)]
|
||||
async fn bind_local(
|
||||
&self,
|
||||
internal_port: u16,
|
||||
address_schema: AddressSchemaLocal,
|
||||
) -> Result<Address, Report> {
|
||||
Err(eyre!("Operation not permitted"))
|
||||
}
|
||||
#[allow(unused_variables)]
|
||||
async fn bind_onion(
|
||||
&self,
|
||||
internal_port: u16,
|
||||
address_schema: AddressSchemaOnion,
|
||||
) -> Result<Address, Report> {
|
||||
Err(eyre!("Operation not permitted"))
|
||||
}
|
||||
#[allow(unused_variables)]
|
||||
async fn unbind_local(&self, id: InterfaceId, external: u16) -> Result<(), Report> {
|
||||
Err(eyre!("Operation not permitted"))
|
||||
}
|
||||
#[allow(unused_variables)]
|
||||
async fn unbind_onion(&self, id: InterfaceId, external: u16) -> Result<(), Report> {
|
||||
Err(eyre!("Operation not permitted"))
|
||||
}
|
||||
fn set_started(&self) -> Result<(), Report> {
|
||||
Err(eyre!("Operation not permitted"))
|
||||
}
|
||||
async fn restart(&self) -> Result<(), Report> {
|
||||
Err(eyre!("Operation not permitted"))
|
||||
}
|
||||
async fn start(&self) -> Result<(), Report> {
|
||||
Err(eyre!("Operation not permitted"))
|
||||
}
|
||||
async fn stop(&self) -> Result<(), Report> {
|
||||
Err(eyre!("Operation not permitted"))
|
||||
}
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct ExecuteArgs {
|
||||
pub procedure: JsProcedure,
|
||||
pub directory: PathBuf,
|
||||
pub pkg_id: PackageId,
|
||||
pub pkg_version: Version,
|
||||
pub name: ProcedureName,
|
||||
pub volumes: Volumes,
|
||||
pub input: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
@@ -120,56 +82,54 @@ impl JsProcedure {
|
||||
volumes: &Volumes,
|
||||
input: Option<I>,
|
||||
timeout: Option<Duration>,
|
||||
gid: ProcessGroupId,
|
||||
rpc_client: Option<Arc<UnixRpcClient>>,
|
||||
os: Arc<dyn OsApi>,
|
||||
_gid: ProcessGroupId,
|
||||
_rpc_client: Option<Arc<UnixRpcClient>>,
|
||||
) -> Result<Result<O, (i32, String)>, Error> {
|
||||
let cleaner_client = rpc_client.clone();
|
||||
let cleaner = GeneralGuard::new(move || {
|
||||
tokio::spawn(async move {
|
||||
if let Some(client) = cleaner_client {
|
||||
client
|
||||
.request(SignalGroup, SignalGroupParams { gid, signal: 9 })
|
||||
.await
|
||||
.map_err(|e| {
|
||||
Error::new(eyre!("{}: {:?}", e.message, e.data), ErrorKind::Docker)
|
||||
})
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
})
|
||||
});
|
||||
let res = async move {
|
||||
let running_action = JsExecutionEnvironment::load_from_package(
|
||||
os,
|
||||
directory,
|
||||
pkg_id,
|
||||
pkg_version,
|
||||
Box::new(volumes.clone()),
|
||||
gid,
|
||||
rpc_client,
|
||||
)
|
||||
.await?
|
||||
.run_action(name, input, self.args.clone());
|
||||
let output: Option<ErrorValue> = match timeout {
|
||||
Some(timeout_duration) => tokio::time::timeout(timeout_duration, running_action)
|
||||
.await
|
||||
.map_err(|_| (JsError::Timeout, "Timed out. Retrying soon...".to_owned()))??,
|
||||
None => running_action.await?,
|
||||
};
|
||||
let output: O = unwrap_known_error(output)?;
|
||||
Ok(output)
|
||||
let runner_argument = ExecuteArgs {
|
||||
procedure: self.clone(),
|
||||
directory: directory.clone(),
|
||||
pkg_id: pkg_id.clone(),
|
||||
pkg_version: pkg_version.clone(),
|
||||
name,
|
||||
volumes: volumes.clone(),
|
||||
input: input.and_then(|x| serde_json::to_value(x).ok()),
|
||||
};
|
||||
let mut runner = Command::new("start-deno")
|
||||
.arg("execute")
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.kill_on_drop(true)
|
||||
.spawn()?;
|
||||
to_json_async_writer(
|
||||
&mut runner.stdin.take().or_not_found("stdin")?,
|
||||
&runner_argument,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let res = if let Some(timeout) = timeout {
|
||||
tokio::time::timeout(timeout, runner.wait_with_output())
|
||||
.await
|
||||
.with_kind(ErrorKind::Timeout)??
|
||||
} else {
|
||||
runner.wait_with_output().await?
|
||||
};
|
||||
|
||||
if res.status.success() {
|
||||
serde_json::from_str::<Result<O, (i32, String)>>(std::str::from_utf8(&res.stdout)?)
|
||||
.with_kind(ErrorKind::Deserialization)
|
||||
} else {
|
||||
Err(Error::new(
|
||||
eyre!("{}", String::from_utf8(res.stderr)?),
|
||||
ErrorKind::Javascript,
|
||||
))
|
||||
}
|
||||
.await
|
||||
.map_err(|(error, message)| (error.as_code_num(), message));
|
||||
cleaner.drop().await.unwrap()?;
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
pub async fn sandboxed<I: Serialize, O: DeserializeOwned>(
|
||||
&self,
|
||||
ctx: &RpcContext,
|
||||
directory: &PathBuf,
|
||||
pkg_id: &PackageId,
|
||||
pkg_version: &Version,
|
||||
volumes: &Volumes,
|
||||
@@ -177,25 +137,97 @@ impl JsProcedure {
|
||||
timeout: Option<Duration>,
|
||||
name: ProcedureName,
|
||||
) -> Result<Result<O, (i32, String)>, Error> {
|
||||
Ok(async move {
|
||||
let runner_argument = ExecuteArgs {
|
||||
procedure: self.clone(),
|
||||
directory: directory.clone(),
|
||||
pkg_id: pkg_id.clone(),
|
||||
pkg_version: pkg_version.clone(),
|
||||
name,
|
||||
volumes: volumes.clone(),
|
||||
input: input.and_then(|x| serde_json::to_value(x).ok()),
|
||||
};
|
||||
let mut runner = Command::new("start-deno")
|
||||
.arg("sandbox")
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.kill_on_drop(true)
|
||||
.spawn()?;
|
||||
to_json_async_writer(
|
||||
&mut runner.stdin.take().or_not_found("stdin")?,
|
||||
&runner_argument,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let res = if let Some(timeout) = timeout {
|
||||
tokio::time::timeout(timeout, runner.wait_with_output())
|
||||
.await
|
||||
.with_kind(ErrorKind::Timeout)??
|
||||
} else {
|
||||
runner.wait_with_output().await?
|
||||
};
|
||||
|
||||
if res.status.success() {
|
||||
serde_json::from_str::<Result<O, (i32, String)>>(std::str::from_utf8(&res.stdout)?)
|
||||
.with_kind(ErrorKind::Deserialization)
|
||||
} else {
|
||||
Err(Error::new(
|
||||
eyre!("{}", String::from_utf8(res.stderr)?),
|
||||
ErrorKind::Javascript,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
pub async fn execute_impl<I: Serialize, O: DeserializeOwned>(
|
||||
&self,
|
||||
directory: &PathBuf,
|
||||
pkg_id: &PackageId,
|
||||
pkg_version: &Version,
|
||||
name: ProcedureName,
|
||||
volumes: &Volumes,
|
||||
input: Option<I>,
|
||||
) -> Result<Result<O, (i32, String)>, Error> {
|
||||
let res = async move {
|
||||
let running_action = JsExecutionEnvironment::load_from_package(
|
||||
Arc::new(SandboxOsApi { _ctx: ctx.clone() }),
|
||||
&ctx.datadir,
|
||||
directory,
|
||||
pkg_id,
|
||||
pkg_version,
|
||||
Box::new(volumes.clone()),
|
||||
)
|
||||
.await?
|
||||
.run_action(name, input, self.args.clone());
|
||||
let output: Option<ErrorValue> = running_action.await?;
|
||||
let output: O = unwrap_known_error(output)?;
|
||||
Ok(output)
|
||||
}
|
||||
.await
|
||||
.map_err(|(error, message)| (error.as_code_num(), message));
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
pub async fn sandboxed_impl<I: Serialize, O: DeserializeOwned>(
|
||||
&self,
|
||||
directory: &PathBuf,
|
||||
pkg_id: &PackageId,
|
||||
pkg_version: &Version,
|
||||
volumes: &Volumes,
|
||||
input: Option<I>,
|
||||
name: ProcedureName,
|
||||
) -> Result<Result<O, (i32, String)>, Error> {
|
||||
Ok(async move {
|
||||
let running_action = JsExecutionEnvironment::load_from_package(
|
||||
directory,
|
||||
pkg_id,
|
||||
pkg_version,
|
||||
Box::new(volumes.clone()),
|
||||
ProcessGroupId(0),
|
||||
None,
|
||||
)
|
||||
.await?
|
||||
.read_only_effects()
|
||||
.run_action(name, input, self.args.clone());
|
||||
let output: Option<ErrorValue> = match timeout {
|
||||
Some(timeout_duration) => tokio::time::timeout(timeout_duration, running_action)
|
||||
.await
|
||||
.map_err(|_| (JsError::Timeout, "Timed out. Retrying soon...".to_owned()))??,
|
||||
None => running_action.await?,
|
||||
};
|
||||
let output: Option<ErrorValue> = running_action.await?;
|
||||
let output: O = unwrap_known_error(output)?;
|
||||
Ok(output)
|
||||
}
|
||||
@@ -873,134 +905,40 @@ mod tests {
|
||||
}
|
||||
}))
|
||||
.unwrap();
|
||||
let input: Option<serde_json::Value> = None;
|
||||
let timeout = Some(Duration::from_secs(10));
|
||||
js_action
|
||||
.execute::<serde_json::Value, serde_json::Value>(
|
||||
&path,
|
||||
&package_id,
|
||||
&package_version,
|
||||
name,
|
||||
&volumes,
|
||||
input,
|
||||
timeout,
|
||||
ProcessGroupId(0),
|
||||
None,
|
||||
Arc::new(OsApiMock::default()),
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
}
|
||||
#[tokio::test]
|
||||
async fn test_callback() {
|
||||
let api = Arc::new(OsApiMock::default());
|
||||
let action_api = api.clone();
|
||||
let spawned = tokio::spawn(async move {
|
||||
let mut watching = api.config_callbacks.subscribe();
|
||||
loop {
|
||||
if watching.borrow().is_empty() {
|
||||
watching.changed().await.unwrap();
|
||||
continue;
|
||||
}
|
||||
api.config_callbacks.send_modify(|x| {
|
||||
x[0].call(vec![json!("This is something across the wire!")])
|
||||
.map_err(|e| format!("Failed call"))
|
||||
.unwrap();
|
||||
});
|
||||
break;
|
||||
}
|
||||
});
|
||||
let js_action = JsProcedure { args: vec![] };
|
||||
let path: PathBuf = "test/js_action_execute/"
|
||||
.parse::<PathBuf>()
|
||||
.unwrap()
|
||||
.canonicalize()
|
||||
.unwrap();
|
||||
let package_id = "test-package".parse().unwrap();
|
||||
let package_version: Version = "0.3.0.3".parse().unwrap();
|
||||
let name = ProcedureName::Action("test-callback".parse().unwrap());
|
||||
let volumes: Volumes = serde_json::from_value(json!({
|
||||
"main": {
|
||||
"type": "data"
|
||||
},
|
||||
"compat": {
|
||||
"type": "assets"
|
||||
},
|
||||
"filebrowser" :{
|
||||
"package-id": "filebrowser",
|
||||
"path": "data",
|
||||
"readonly": true,
|
||||
"type": "pointer",
|
||||
"volume-id": "main",
|
||||
}
|
||||
}))
|
||||
let package_id = "test-package".parse().unwrap();
|
||||
let package_version: Version = "0.3.0.3".parse().unwrap();
|
||||
let name = ProcedureName::Action("test-disk-usage".parse().unwrap());
|
||||
let volumes: Volumes = serde_json::from_value(serde_json::json!({
|
||||
"main": {
|
||||
"type": "data"
|
||||
},
|
||||
"compat": {
|
||||
"type": "assets"
|
||||
},
|
||||
"filebrowser" :{
|
||||
"package-id": "filebrowser",
|
||||
"path": "data",
|
||||
"readonly": true,
|
||||
"type": "pointer",
|
||||
"volume-id": "main",
|
||||
}
|
||||
}))
|
||||
.unwrap();
|
||||
let input: Option<serde_json::Value> = None;
|
||||
let timeout = Some(Duration::from_secs(10));
|
||||
js_action
|
||||
.execute::<serde_json::Value, serde_json::Value>(
|
||||
&path,
|
||||
&package_id,
|
||||
&package_version,
|
||||
name,
|
||||
&volumes,
|
||||
input,
|
||||
timeout,
|
||||
ProcessGroupId(0),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
let input: Option<serde_json::Value> = None;
|
||||
let timeout = Some(Duration::from_secs(10));
|
||||
js_action
|
||||
.execute::<serde_json::Value, serde_json::Value>(
|
||||
&path,
|
||||
&package_id,
|
||||
&package_version,
|
||||
name,
|
||||
&volumes,
|
||||
input,
|
||||
timeout,
|
||||
ProcessGroupId(0),
|
||||
None,
|
||||
action_api,
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
spawned.await.unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn js_disk_usage() {
|
||||
let js_action = JsProcedure { args: vec![] };
|
||||
let path: PathBuf = "test/js_action_execute/"
|
||||
.parse::<PathBuf>()
|
||||
.unwrap()
|
||||
.canonicalize()
|
||||
.unwrap();
|
||||
let package_id = "test-package".parse().unwrap();
|
||||
let package_version: Version = "0.3.0.3".parse().unwrap();
|
||||
let name = ProcedureName::Action("test-disk-usage".parse().unwrap());
|
||||
let volumes: Volumes = serde_json::from_value(serde_json::json!({
|
||||
"main": {
|
||||
"type": "data"
|
||||
},
|
||||
"compat": {
|
||||
"type": "assets"
|
||||
},
|
||||
"filebrowser" :{
|
||||
"package-id": "filebrowser",
|
||||
"path": "data",
|
||||
"readonly": true,
|
||||
"type": "pointer",
|
||||
"volume-id": "main",
|
||||
}
|
||||
}))
|
||||
.unwrap();
|
||||
let input: Option<serde_json::Value> = None;
|
||||
let timeout = Some(Duration::from_secs(10));
|
||||
dbg!(js_action
|
||||
.execute::<serde_json::Value, serde_json::Value>(
|
||||
&path,
|
||||
&package_id,
|
||||
&package_version,
|
||||
name,
|
||||
&volumes,
|
||||
input,
|
||||
timeout,
|
||||
ProcessGroupId(0),
|
||||
None,
|
||||
Arc::new(OsApiMock::default()),
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,8 +21,6 @@ pub mod docker;
|
||||
pub mod js_scripts;
|
||||
pub use models::ProcedureName;
|
||||
|
||||
// TODO: create RPC endpoint that looks up the appropriate action and calls `execute`
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, HasModel)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[serde(tag = "type")]
|
||||
@@ -140,7 +138,15 @@ impl PackageProcedure {
|
||||
#[cfg(feature = "js_engine")]
|
||||
PackageProcedure::Script(procedure) => {
|
||||
procedure
|
||||
.sandboxed(ctx, pkg_id, pkg_version, volumes, input, timeout, name)
|
||||
.sandboxed(
|
||||
&ctx.datadir,
|
||||
pkg_id,
|
||||
pkg_version,
|
||||
volumes,
|
||||
input,
|
||||
timeout,
|
||||
name,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
@@ -158,13 +164,15 @@ impl std::fmt::Display for PackageProcedure {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: make this not allocate
|
||||
#[derive(Debug)]
|
||||
pub struct NoOutput;
|
||||
impl<'de> Deserialize<'de> for NoOutput {
|
||||
fn deserialize<D>(_: D) -> Result<Self, D::Error>
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
let _ = Value::deserialize(deserializer)?;
|
||||
Ok(NoOutput)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ pub async fn properties(#[context] ctx: RpcContext, #[arg] id: PackageId) -> Res
|
||||
|
||||
#[instrument(skip_all)]
|
||||
pub async fn fetch_properties(ctx: RpcContext, id: PackageId) -> Result<Value, Error> {
|
||||
let peek = ctx.db.peek().await?;
|
||||
let peek = ctx.db.peek().await;
|
||||
|
||||
let manifest = peek
|
||||
.as_package_data()
|
||||
|
||||
@@ -52,7 +52,7 @@ async fn do_index(
|
||||
pkg: &Package,
|
||||
) -> Result<(), Error> {
|
||||
url.set_path("/admin/v0/index");
|
||||
let mut req = httpc
|
||||
let req = httpc
|
||||
.post(url)
|
||||
.header(header::ACCEPT, "text/plain")
|
||||
.basic_auth(user, Some(pass))
|
||||
@@ -74,7 +74,7 @@ async fn do_upload(
|
||||
body: Body,
|
||||
) -> Result<(), Error> {
|
||||
url.set_path("/admin/v0/upload");
|
||||
let mut req = httpc
|
||||
let req = httpc
|
||||
.post(url)
|
||||
.header(header::ACCEPT, "text/plain")
|
||||
.basic_auth(user, Some(pass))
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use base64::Engine;
|
||||
use color_eyre::eyre::eyre;
|
||||
use reqwest::{StatusCode, Url};
|
||||
use rpc_toolkit::command;
|
||||
@@ -65,12 +66,11 @@ pub async fn get(#[context] ctx: RpcContext, #[arg] url: Url) -> Result<Value, E
|
||||
Some(ctype) => Ok(Value::String(format!(
|
||||
"data:{};base64,{}",
|
||||
ctype,
|
||||
base64::encode_config(
|
||||
base64::engine::general_purpose::URL_SAFE.encode(
|
||||
&response
|
||||
.bytes()
|
||||
.await
|
||||
.with_kind(crate::ErrorKind::Registry)?,
|
||||
base64::URL_SAFE
|
||||
.with_kind(crate::ErrorKind::Registry)?
|
||||
)
|
||||
))),
|
||||
_ => Err(Error::new(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use sha2_old::{Digest, Sha512};
|
||||
use sha2::{Digest, Sha512};
|
||||
use tokio::io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt, SeekFrom};
|
||||
use tracing::instrument;
|
||||
use typed_builder::TypedBuilder;
|
||||
@@ -43,7 +43,7 @@ impl<
|
||||
{
|
||||
/// BLOCKING
|
||||
#[instrument(skip_all)]
|
||||
pub async fn pack(mut self, key: &ed25519_dalek::Keypair) -> Result<(), Error> {
|
||||
pub async fn pack(mut self, key: &ed25519_dalek::SigningKey) -> Result<(), Error> {
|
||||
let header_pos = self.writer.stream_position().await?;
|
||||
if header_pos != 0 {
|
||||
tracing::warn!("Appending to non-empty file.");
|
||||
@@ -132,7 +132,7 @@ impl<
|
||||
// header
|
||||
let (hash, _) = writer.finish();
|
||||
self.writer.seek(SeekFrom::Start(header_pos)).await?;
|
||||
header.pubkey = key.public.clone();
|
||||
header.pubkey = key.into();
|
||||
header.signature = key.sign_prehashed(hash, Some(SIG_CONTEXT))?;
|
||||
header
|
||||
.serialize(&mut self.writer)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use color_eyre::eyre::eyre;
|
||||
use ed25519_dalek::{PublicKey, Signature};
|
||||
use ed25519_dalek::{Signature, VerifyingKey};
|
||||
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWriteExt};
|
||||
|
||||
use crate::Error;
|
||||
@@ -11,15 +11,15 @@ pub const VERSION: u8 = 1;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Header {
|
||||
pub pubkey: PublicKey,
|
||||
pub pubkey: VerifyingKey,
|
||||
pub signature: Signature,
|
||||
pub table_of_contents: TableOfContents,
|
||||
}
|
||||
impl Header {
|
||||
pub fn placeholder() -> Self {
|
||||
Header {
|
||||
pubkey: PublicKey::default(),
|
||||
signature: Signature::from_bytes(&[0; 64]).expect("Invalid ed25519 signature"),
|
||||
pubkey: VerifyingKey::default(),
|
||||
signature: Signature::from_bytes(&[0; 64]),
|
||||
table_of_contents: Default::default(),
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ impl Header {
|
||||
writer.write_all(&MAGIC).await?;
|
||||
writer.write_all(&[VERSION]).await?;
|
||||
writer.write_all(self.pubkey.as_bytes()).await?;
|
||||
writer.write_all(self.signature.as_ref()).await?;
|
||||
writer.write_all(&self.signature.to_bytes()).await?;
|
||||
self.table_of_contents.serialize(writer).await?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -51,11 +51,11 @@ impl Header {
|
||||
}
|
||||
let mut pubkey_bytes = [0; 32];
|
||||
reader.read_exact(&mut pubkey_bytes).await?;
|
||||
let pubkey = PublicKey::from_bytes(&pubkey_bytes)
|
||||
let pubkey = VerifyingKey::from_bytes(&pubkey_bytes)
|
||||
.map_err(|e| Error::new(e, crate::ErrorKind::ParseS9pk))?;
|
||||
let mut sig_bytes = [0; 64];
|
||||
reader.read_exact(&mut sig_bytes).await?;
|
||||
let signature = Signature::from_bytes(&sig_bytes).expect("Invalid ed25519 signature");
|
||||
let signature = Signature::from_bytes(&sig_bytes);
|
||||
let table_of_contents = TableOfContents::deserialize(reader).await?;
|
||||
|
||||
Ok(Header {
|
||||
|
||||
@@ -7,11 +7,11 @@ use std::str::FromStr;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
use color_eyre::eyre::eyre;
|
||||
use digest_old::Output;
|
||||
use ed25519_dalek::PublicKey;
|
||||
use digest::Output;
|
||||
use ed25519_dalek::VerifyingKey;
|
||||
use futures::TryStreamExt;
|
||||
use models::ImageId;
|
||||
use sha2_old::{Digest, Sha512};
|
||||
use sha2::{Digest, Sha512};
|
||||
use tokio::fs::File;
|
||||
use tokio::io::{AsyncRead, AsyncReadExt, AsyncSeek, AsyncSeekExt, ReadBuf};
|
||||
use tracing::instrument;
|
||||
@@ -147,7 +147,7 @@ impl FromStr for ImageTag {
|
||||
pub struct S9pkReader<R: AsyncRead + AsyncSeek + Unpin + Send + Sync = File> {
|
||||
hash: Option<Output<Sha512>>,
|
||||
hash_string: Option<String>,
|
||||
developer_key: PublicKey,
|
||||
developer_key: VerifyingKey,
|
||||
toc: TableOfContents,
|
||||
pos: u64,
|
||||
rdr: R,
|
||||
@@ -333,7 +333,7 @@ impl<R: AsyncRead + AsyncSeek + Unpin + Send + Sync> S9pkReader<R> {
|
||||
self.hash_string.as_ref().map(|s| s.as_str())
|
||||
}
|
||||
|
||||
pub fn developer_key(&self) -> &PublicKey {
|
||||
pub fn developer_key(&self) -> &VerifyingKey {
|
||||
&self.developer_key
|
||||
}
|
||||
|
||||
|
||||
@@ -59,11 +59,11 @@ async fn setup_init(
|
||||
let mut secrets_handle = secret_store.acquire().await?;
|
||||
let mut secrets_tx = secrets_handle.begin().await?;
|
||||
|
||||
let mut account = AccountInfo::load(&mut secrets_tx).await?;
|
||||
let mut account = AccountInfo::load(secrets_tx.as_mut()).await?;
|
||||
|
||||
if let Some(password) = password {
|
||||
account.set_password(&password)?;
|
||||
account.save(&mut secrets_tx).await?;
|
||||
account.save(secrets_tx.as_mut()).await?;
|
||||
db.mutate(|m| {
|
||||
m.as_server_info_mut()
|
||||
.as_password_hash_mut()
|
||||
|
||||
@@ -13,8 +13,7 @@ use crate::{Error, OS_ARCH};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Shutdown {
|
||||
pub datadir: PathBuf,
|
||||
pub disk_guid: Option<Arc<String>>,
|
||||
pub export_args: Option<(Arc<String>, PathBuf)>,
|
||||
pub restart: bool,
|
||||
}
|
||||
impl Shutdown {
|
||||
@@ -55,8 +54,8 @@ impl Shutdown {
|
||||
tracing::debug!("{:?}", e);
|
||||
}
|
||||
}
|
||||
if let Some(guid) = &self.disk_guid {
|
||||
if let Err(e) = export(guid, &self.datadir).await {
|
||||
if let Some((guid, datadir)) = &self.export_args {
|
||||
if let Err(e) = export(guid, datadir).await {
|
||||
tracing::error!("Error Exporting Volume Group: {}", e);
|
||||
tracing::debug!("{:?}", e);
|
||||
}
|
||||
@@ -93,8 +92,7 @@ impl Shutdown {
|
||||
pub async fn shutdown(#[context] ctx: RpcContext) -> Result<(), Error> {
|
||||
ctx.shutdown
|
||||
.send(Some(Shutdown {
|
||||
datadir: ctx.datadir.clone(),
|
||||
disk_guid: Some(ctx.disk_guid.clone()),
|
||||
export_args: Some((ctx.disk_guid.clone(), ctx.datadir.clone())),
|
||||
restart: false,
|
||||
}))
|
||||
.map_err(|_| ())
|
||||
@@ -106,8 +104,7 @@ pub async fn shutdown(#[context] ctx: RpcContext) -> Result<(), Error> {
|
||||
pub async fn restart(#[context] ctx: RpcContext) -> Result<(), Error> {
|
||||
ctx.shutdown
|
||||
.send(Some(Shutdown {
|
||||
datadir: ctx.datadir.clone(),
|
||||
disk_guid: Some(ctx.disk_guid.clone()),
|
||||
export_args: Some((ctx.disk_guid.clone(), ctx.datadir.clone())),
|
||||
restart: true,
|
||||
}))
|
||||
.map_err(|_| ())
|
||||
|
||||
@@ -16,8 +16,6 @@ pub struct Status {
|
||||
pub configured: bool,
|
||||
pub main: MainStatus,
|
||||
#[serde(default)]
|
||||
pub dependency_errors: BTreeMap<(), ()>, // TODO: remove
|
||||
#[serde(default)]
|
||||
pub dependency_config_errors: DependencyConfigErrors,
|
||||
}
|
||||
|
||||
|
||||
@@ -23,8 +23,6 @@ use crate::util::serde::{display_serializable, IoFormat};
|
||||
use crate::util::{display_none, Invoke};
|
||||
use crate::{Error, ErrorKind, ResultExt};
|
||||
|
||||
pub const SYSTEMD_UNIT: &'static str = "startd";
|
||||
|
||||
#[command(subcommands(zram))]
|
||||
pub async fn experimental() -> Result<(), Error> {
|
||||
Ok(())
|
||||
@@ -60,7 +58,7 @@ pub async fn enable_zram() -> Result<(), Error> {
|
||||
|
||||
#[command(display(display_none))]
|
||||
pub async fn zram(#[context] ctx: RpcContext, #[arg] enable: bool) -> Result<(), Error> {
|
||||
let db = ctx.db.peek().await?;
|
||||
let db = ctx.db.peek().await;
|
||||
|
||||
let zram = db.as_server_info().as_zram().de()?;
|
||||
if enable == zram {
|
||||
@@ -130,7 +128,7 @@ pub async fn logs_nofollow(
|
||||
_ctx: (),
|
||||
(limit, cursor, before, _): (Option<usize>, Option<String>, bool, bool),
|
||||
) -> Result<LogResponse, Error> {
|
||||
fetch_logs(LogSource::Service(SYSTEMD_UNIT), limit, cursor, before).await
|
||||
fetch_logs(LogSource::System, limit, cursor, before).await
|
||||
}
|
||||
|
||||
#[command(rpc_only, rename = "follow", display(display_none))]
|
||||
@@ -138,7 +136,7 @@ pub async fn logs_follow(
|
||||
#[context] ctx: RpcContext,
|
||||
#[parent_data] (limit, _, _, _): (Option<usize>, Option<String>, bool, bool),
|
||||
) -> Result<LogFollowResponse, Error> {
|
||||
follow_logs(ctx, LogSource::Service(SYSTEMD_UNIT), limit).await
|
||||
follow_logs(ctx, LogSource::System, limit).await
|
||||
}
|
||||
|
||||
#[command(
|
||||
@@ -590,7 +588,8 @@ async fn get_temp() -> Result<Celsius, Error> {
|
||||
.flat_map(|(_, v)| v.as_object())
|
||||
.flatten()
|
||||
.filter_map(|(k, v)| {
|
||||
if k.ends_with("_input") {
|
||||
// we have seen so far that `temp1` is always a composite reading of some sort, so we should just use that for each chip
|
||||
if k.trim() == "temp1_input" {
|
||||
v.as_f64()
|
||||
} else {
|
||||
None
|
||||
|
||||
@@ -76,7 +76,7 @@ fn display_update_result(status: UpdateResult, _: &ArgMatches) {
|
||||
|
||||
#[instrument(skip_all)]
|
||||
async fn maybe_do_update(ctx: RpcContext, marketplace_url: Url) -> Result<Option<()>, Error> {
|
||||
let peeked = ctx.db.peek().await?;
|
||||
let peeked = ctx.db.peek().await;
|
||||
let latest_version: Version = ctx
|
||||
.client
|
||||
.get(with_query_params(
|
||||
@@ -154,7 +154,7 @@ async fn maybe_do_update(ctx: RpcContext, marketplace_url: Url) -> Result<Option
|
||||
ctx.db.clone(),
|
||||
None,
|
||||
NotificationLevel::Error,
|
||||
"embassyOS Update Failed".to_owned(),
|
||||
"StartOS Update Failed".to_owned(),
|
||||
format!("Update was not successful because of {}", e),
|
||||
(),
|
||||
None,
|
||||
|
||||
125
backend/src/util/cpupower.rs
Normal file
125
backend/src/util/cpupower.rs
Normal file
@@ -0,0 +1,125 @@
|
||||
use std::borrow::Cow;
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use imbl::OrdMap;
|
||||
use tokio::process::Command;
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::util::Invoke;
|
||||
|
||||
pub const GOVERNOR_PERFORMANCE: Governor = Governor(Cow::Borrowed("performance"));
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Governor(Cow<'static, str>);
|
||||
impl std::fmt::Display for Governor {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
impl std::ops::Deref for Governor {
|
||||
type Target = str;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&*self.0
|
||||
}
|
||||
}
|
||||
impl std::borrow::Borrow<str> for Governor {
|
||||
fn borrow(&self) -> &str {
|
||||
&**self
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_available_governors() -> Result<BTreeSet<Governor>, Error> {
|
||||
let raw = String::from_utf8(
|
||||
Command::new("cpupower")
|
||||
.arg("frequency-info")
|
||||
.arg("-g")
|
||||
.invoke(ErrorKind::CpuSettings)
|
||||
.await?,
|
||||
)?;
|
||||
let mut for_cpu: OrdMap<u32, BTreeSet<Governor>> = OrdMap::new();
|
||||
let mut current_cpu = None;
|
||||
for line in raw.lines() {
|
||||
if line.starts_with("analyzing") {
|
||||
current_cpu = Some(
|
||||
sscanf::sscanf!(line, "analyzing CPU {u32}:")
|
||||
.map_err(|e| eyre!("{e}"))
|
||||
.with_kind(ErrorKind::ParseSysInfo)?,
|
||||
);
|
||||
} else if let Some(rest) = line
|
||||
.trim()
|
||||
.strip_prefix("available cpufreq governors:")
|
||||
.map(|s| s.trim())
|
||||
{
|
||||
if rest != "Not Available" {
|
||||
for_cpu
|
||||
.entry(current_cpu.ok_or_else(|| {
|
||||
Error::new(
|
||||
eyre!("governors listed before cpu"),
|
||||
ErrorKind::ParseSysInfo,
|
||||
)
|
||||
})?)
|
||||
.or_default()
|
||||
.extend(
|
||||
rest.split_ascii_whitespace()
|
||||
.map(|g| Governor(Cow::Owned(g.to_owned()))),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(for_cpu
|
||||
.into_iter()
|
||||
.fold(None, |acc: Option<BTreeSet<Governor>>, (_, x)| {
|
||||
if let Some(acc) = acc {
|
||||
Some(acc.intersection(&x).cloned().collect())
|
||||
} else {
|
||||
Some(x)
|
||||
}
|
||||
})
|
||||
.unwrap_or_default()) // include only governors available for ALL cpus
|
||||
}
|
||||
|
||||
pub async fn current_governor() -> Result<Option<Governor>, Error> {
|
||||
let Some(raw) = Command::new("cpupower")
|
||||
.arg("frequency-info")
|
||||
.arg("-p")
|
||||
.invoke(ErrorKind::CpuSettings)
|
||||
.await
|
||||
.and_then(|s| Ok(Some(String::from_utf8(s)?)))
|
||||
.or_else(|e| {
|
||||
if e.source
|
||||
.to_string()
|
||||
.contains("Unable to determine current policy")
|
||||
{
|
||||
Ok(None)
|
||||
} else {
|
||||
Err(e)
|
||||
}
|
||||
})?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
for line in raw.lines() {
|
||||
if let Some(governor) = line
|
||||
.trim()
|
||||
.strip_prefix("The governor \"")
|
||||
.and_then(|s| s.strip_suffix("\" may decide which speed to use"))
|
||||
{
|
||||
return Ok(Some(Governor(Cow::Owned(governor.to_owned()))));
|
||||
}
|
||||
}
|
||||
Err(Error::new(
|
||||
eyre!("Failed to parse cpupower output:\n{raw}"),
|
||||
ErrorKind::ParseSysInfo,
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn set_governor(governor: &Governor) -> Result<(), Error> {
|
||||
Command::new("cpupower")
|
||||
.arg("frequency-set")
|
||||
.arg("-g")
|
||||
.arg(&*governor.0)
|
||||
.invoke(ErrorKind::CpuSettings)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
13
backend/src/util/crypto.rs
Normal file
13
backend/src/util/crypto.rs
Normal file
@@ -0,0 +1,13 @@
|
||||
use ed25519_dalek::hazmat::ExpandedSecretKey;
|
||||
use ed25519_dalek::{SecretKey, EXPANDED_SECRET_KEY_LENGTH};
|
||||
|
||||
#[inline]
|
||||
pub fn ed25519_expand_key(key: &SecretKey) -> [u8; EXPANDED_SECRET_KEY_LENGTH] {
|
||||
let key = ExpandedSecretKey::from(key);
|
||||
|
||||
let mut bytes: [u8; 64] = [0u8; 64];
|
||||
|
||||
bytes[..32].copy_from_slice(key.scalar.as_bytes());
|
||||
bytes[32..].copy_from_slice(&key.hash_prefix[..]);
|
||||
bytes
|
||||
}
|
||||
@@ -143,7 +143,7 @@ where
|
||||
{
|
||||
let mut buffer = Vec::new();
|
||||
reader.read_to_end(&mut buffer).await?;
|
||||
serde_toml::from_slice(&buffer)
|
||||
serde_toml::from_str(std::str::from_utf8(&buffer)?)
|
||||
.map_err(color_eyre::eyre::Error::from)
|
||||
.with_kind(crate::ErrorKind::Deserialization)
|
||||
}
|
||||
@@ -153,7 +153,9 @@ where
|
||||
T: serde::Serialize,
|
||||
W: AsyncWrite + Unpin,
|
||||
{
|
||||
let mut buffer = serde_toml::to_vec(value).with_kind(crate::ErrorKind::Serialization)?;
|
||||
let mut buffer = serde_toml::to_string(value)
|
||||
.with_kind(crate::ErrorKind::Serialization)?
|
||||
.into_bytes();
|
||||
buffer.extend_from_slice(b"\n");
|
||||
writer.write_all(&buffer).await?;
|
||||
Ok(())
|
||||
|
||||
@@ -10,13 +10,28 @@ impl EmbassyLogger {
|
||||
use tracing_subscriber::prelude::*;
|
||||
use tracing_subscriber::{fmt, EnvFilter};
|
||||
|
||||
let filter_layer = EnvFilter::from_default_env();
|
||||
let filter_layer = EnvFilter::builder()
|
||||
.with_default_directive(
|
||||
format!("{}=info", std::module_path!().split("::").next().unwrap())
|
||||
.parse()
|
||||
.unwrap(),
|
||||
)
|
||||
.from_env_lossy();
|
||||
#[cfg(feature = "unstable")]
|
||||
let filter_layer = filter_layer
|
||||
.add_directive("tokio=trace".parse().unwrap())
|
||||
.add_directive("runtime=trace".parse().unwrap());
|
||||
let fmt_layer = fmt::layer().with_target(true);
|
||||
|
||||
tracing_subscriber::registry()
|
||||
let sub = tracing_subscriber::registry()
|
||||
.with(filter_layer)
|
||||
.with(fmt_layer)
|
||||
.with(ErrorLayer::default())
|
||||
.with(ErrorLayer::default());
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
let sub = sub.with(console_subscriber::spawn());
|
||||
|
||||
sub
|
||||
}
|
||||
pub fn init() -> Self {
|
||||
Self::base_subscriber().init();
|
||||
|
||||
@@ -6,6 +6,7 @@ use std::pin::Pin;
|
||||
use std::process::Stdio;
|
||||
use std::sync::Arc;
|
||||
use std::task::{Context, Poll};
|
||||
use std::time::Duration;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use clap::ArgMatches;
|
||||
@@ -16,7 +17,7 @@ pub use helpers::NonDetachingJoinHandle;
|
||||
use lazy_static::lazy_static;
|
||||
pub use models::Version;
|
||||
use pin_project::pin_project;
|
||||
use sha2_old::Digest;
|
||||
use sha2::Digest;
|
||||
use tokio::fs::File;
|
||||
use tokio::sync::{Mutex, OwnedMutexGuard, RwLock};
|
||||
use tracing::instrument;
|
||||
@@ -24,12 +25,14 @@ use tracing::instrument;
|
||||
use crate::shutdown::Shutdown;
|
||||
use crate::{Error, ErrorKind, ResultExt as _};
|
||||
pub mod config;
|
||||
pub mod cpupower;
|
||||
pub mod docker;
|
||||
pub mod http_reader;
|
||||
pub mod io;
|
||||
pub mod logger;
|
||||
pub mod lshw;
|
||||
pub mod serde;
|
||||
pub mod crypto;
|
||||
|
||||
#[derive(Clone, Copy, Debug, ::serde::Deserialize, ::serde::Serialize)]
|
||||
pub enum Never {}
|
||||
@@ -49,13 +52,31 @@ impl std::error::Error for Never {}
|
||||
#[async_trait::async_trait]
|
||||
pub trait Invoke {
|
||||
async fn invoke(&mut self, error_kind: crate::ErrorKind) -> Result<Vec<u8>, Error>;
|
||||
async fn invoke_timeout(
|
||||
&mut self,
|
||||
error_kind: crate::ErrorKind,
|
||||
timeout: Option<Duration>,
|
||||
) -> Result<Vec<u8>, Error>;
|
||||
}
|
||||
#[async_trait::async_trait]
|
||||
impl Invoke for tokio::process::Command {
|
||||
async fn invoke(&mut self, error_kind: crate::ErrorKind) -> Result<Vec<u8>, Error> {
|
||||
self.invoke_timeout(error_kind, None).await
|
||||
}
|
||||
async fn invoke_timeout(
|
||||
&mut self,
|
||||
error_kind: crate::ErrorKind,
|
||||
timeout: Option<Duration>,
|
||||
) -> Result<Vec<u8>, Error> {
|
||||
self.kill_on_drop(true);
|
||||
self.stdout(Stdio::piped());
|
||||
self.stderr(Stdio::piped());
|
||||
let res = self.output().await?;
|
||||
let res = match timeout {
|
||||
None => self.output().await?,
|
||||
Some(t) => tokio::time::timeout(t, self.output())
|
||||
.await
|
||||
.with_kind(ErrorKind::Timeout)??,
|
||||
};
|
||||
crate::ensure_code!(
|
||||
res.status.success(),
|
||||
error_kind,
|
||||
|
||||
@@ -97,20 +97,25 @@ pub fn serialize_display_opt<T: std::fmt::Display, S: Serializer>(
|
||||
}
|
||||
|
||||
pub mod ed25519_pubkey {
|
||||
use ed25519_dalek::PublicKey;
|
||||
use ed25519_dalek::VerifyingKey;
|
||||
use serde::de::{Error, Unexpected, Visitor};
|
||||
use serde::{Deserializer, Serializer};
|
||||
|
||||
pub fn serialize<S: Serializer>(pubkey: &PublicKey, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
pub fn serialize<S: Serializer>(
|
||||
pubkey: &VerifyingKey,
|
||||
serializer: S,
|
||||
) -> Result<S::Ok, S::Error> {
|
||||
serializer.serialize_str(&base32::encode(
|
||||
base32::Alphabet::RFC4648 { padding: true },
|
||||
pubkey.as_bytes(),
|
||||
))
|
||||
}
|
||||
pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<PublicKey, D::Error> {
|
||||
pub fn deserialize<'de, D: Deserializer<'de>>(
|
||||
deserializer: D,
|
||||
) -> Result<VerifyingKey, D::Error> {
|
||||
struct PubkeyVisitor;
|
||||
impl<'de> Visitor<'de> for PubkeyVisitor {
|
||||
type Value = ed25519_dalek::PublicKey;
|
||||
type Value = ed25519_dalek::VerifyingKey;
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(formatter, "an RFC4648 encoded string")
|
||||
}
|
||||
@@ -118,10 +123,13 @@ pub mod ed25519_pubkey {
|
||||
where
|
||||
E: Error,
|
||||
{
|
||||
PublicKey::from_bytes(
|
||||
&base32::decode(base32::Alphabet::RFC4648 { padding: true }, v).ok_or(
|
||||
Error::invalid_value(Unexpected::Str(v), &"an RFC4648 encoded string"),
|
||||
)?,
|
||||
VerifyingKey::from_bytes(
|
||||
&<[u8; 32]>::try_from(
|
||||
base32::decode(base32::Alphabet::RFC4648 { padding: true }, v).ok_or(
|
||||
Error::invalid_value(Unexpected::Str(v), &"an RFC4648 encoded string"),
|
||||
)?,
|
||||
)
|
||||
.map_err(|e| Error::invalid_length(e.len(), &"32 bytes"))?,
|
||||
)
|
||||
.map_err(Error::custom)
|
||||
}
|
||||
@@ -312,11 +320,12 @@ impl IoFormat {
|
||||
.with_kind(crate::ErrorKind::Serialization),
|
||||
IoFormat::Toml => writer
|
||||
.write_all(
|
||||
&serde_toml::to_vec(
|
||||
serde_toml::to_string(
|
||||
&serde_toml::Value::try_from(value)
|
||||
.with_kind(crate::ErrorKind::Serialization)?,
|
||||
)
|
||||
.with_kind(crate::ErrorKind::Serialization)?,
|
||||
.with_kind(crate::ErrorKind::Serialization)?
|
||||
.as_bytes(),
|
||||
)
|
||||
.with_kind(crate::ErrorKind::Serialization),
|
||||
IoFormat::TomlPretty => writer
|
||||
@@ -346,10 +355,11 @@ impl IoFormat {
|
||||
.with_kind(crate::ErrorKind::Serialization)?;
|
||||
Ok(res)
|
||||
}
|
||||
IoFormat::Toml => serde_toml::to_vec(
|
||||
IoFormat::Toml => serde_toml::to_string(
|
||||
&serde_toml::Value::try_from(value).with_kind(crate::ErrorKind::Serialization)?,
|
||||
)
|
||||
.with_kind(crate::ErrorKind::Serialization),
|
||||
.with_kind(crate::ErrorKind::Serialization)
|
||||
.map(|s| s.into_bytes()),
|
||||
IoFormat::TomlPretty => serde_toml::to_string_pretty(
|
||||
&serde_toml::Value::try_from(value).with_kind(crate::ErrorKind::Serialization)?,
|
||||
)
|
||||
@@ -408,7 +418,8 @@ impl IoFormat {
|
||||
serde_cbor::de::from_reader(slice).with_kind(crate::ErrorKind::Deserialization)
|
||||
}
|
||||
IoFormat::Toml | IoFormat::TomlPretty => {
|
||||
serde_toml::from_slice(slice).with_kind(crate::ErrorKind::Deserialization)
|
||||
serde_toml::from_str(std::str::from_utf8(slice)?)
|
||||
.with_kind(crate::ErrorKind::Deserialization)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user