Compare commits

..

21 Commits

Author SHA1 Message Date
charlesgauthereau 4de2b81b33 chore(release): 1.1.2-rc.2 2026-02-16 09:00:26 +01:00
charlesgauthereau b06f9d8186 fix: failed backup 2026-02-16 09:00:18 +01:00
charlesgauthereau f5a2182723 fix: blocking for mysql database 2026-02-16 08:43:27 +01:00
charlesgauthereau e8a96980f1 chore(release): 1.1.2-rc.1 2026-02-16 08:43:13 +01:00
charlesgauthereau a64792bcc6 fix: blocking for mysql database 2026-02-16 08:41:44 +01:00
charlesgauthereau 2d4542be5f chore: Cargo.lock 2026-02-16 08:21:32 +01:00
charlesgauthereau 4a00ed574d chore(release): 1.1.1 2026-02-16 08:21:10 +01:00
Charles GTE fd220598fc Merge pull request #12 from Portabase/fix/s3-region
fix/s3-region
2026-02-16 08:19:11 +01:00
charlesgauthereau d8b239a123 fix: s3 region to us-east-1 2026-02-16 08:18:14 +01:00
Charles GTE ebf0526366 Update README.md 2026-02-15 11:52:35 +01:00
Charles GTE 6461c1523b Merge pull request #11 from Portabase/dev
dev
2026-02-15 11:51:51 +01:00
charlesgauthereau 2809373de1 feat: README.md and docker-compose.yml 2026-02-15 11:50:16 +01:00
charlesgauthereau 5349e1fef9 chore(release): 1.1.0 2026-02-13 14:37:18 +01:00
charlesgauthereau 2bcb393d60 feat: Adding AES GCM encryption. 2026-02-13 14:36:54 +01:00
Charles GTE 406273796e Merge pull request #10 from Portabase/dev
dev
2026-02-13 14:36:19 +01:00
charlesgauthereau cb35347cb6 chore(release): 1.1.0-rc.2 2026-02-12 20:24:21 +01:00
Charles GTE b92138fd84 Merge pull request #9 from Portabase/fix/large-backup-v2
fix/large-backup-v2
2026-02-12 20:22:23 +01:00
charlesgauthereau f4c54836f1 feat: Adding AES GCM encryption. 2026-02-12 20:21:07 +01:00
charlesgauthereau e678cf4b06 feat: Adding AES GCM encryption. 2026-02-12 19:49:17 +01:00
charlesgauthereau 3c599e94de feat: Adding AES GCM encryption. 2026-02-12 19:16:52 +01:00
charlesgauthereau 6bbc856f62 feat: Adding AES GCM encryption. 2026-02-12 18:45:38 +01:00
16 changed files with 749 additions and 291 deletions
+2 -2
View File
@@ -22,5 +22,5 @@ keywords:
- self-hosted
- portabase
license: Apache-2.0
version: 1.1.0-rc.1
date-released: "2026-02-10"
version: 1.1.2-rc.2
date-released: "2026-02-16"
Generated
+431 -82
View File
@@ -8,6 +8,41 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "aead"
version = "0.6.0-rc.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b657e772794c6b04730ea897b66a058ccd866c16d1967da05eeeecec39043fe"
dependencies = [
"crypto-common 0.2.0",
"inout",
]
[[package]]
name = "aes"
version = "0.9.0-rc.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04097e08a47d9ad181c2e1f4a5fabc9ae06ce8839a333ba9a949bcb0d31fd2a3"
dependencies = [
"cipher",
"cpubits",
"cpufeatures 0.2.17",
]
[[package]]
name = "aes-gcm"
version = "0.11.0-rc.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e22c0c90bbe8d4f77c3ca9ddabe41a1f8382d6fc1f7cea89459d0f320371f972"
dependencies = [
"aead",
"aes",
"cipher",
"ctr",
"ghash",
"subtle",
]
[[package]]
name = "ahash"
version = "0.8.12"
@@ -128,9 +163,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "aws-config"
version = "1.8.13"
version = "1.8.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c456581cb3c77fafcc8c67204a70680d40b61112d6da78c77bd31d945b65f1b5"
checksum = "8a8fc176d53d6fe85017f230405e3255cedb4a02221cb55ed6d76dccbbb099b2"
dependencies = [
"aws-credential-types",
"aws-runtime",
@@ -158,9 +193,9 @@ dependencies = [
[[package]]
name = "aws-credential-types"
version = "1.2.11"
version = "1.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cd362783681b15d136480ad555a099e82ecd8e2d10a841e14dfd0078d67fee3"
checksum = "e26bbf46abc608f2dc61fd6cb3b7b0665497cc259a21520151ed98f8b37d2c79"
dependencies = [
"aws-smithy-async",
"aws-smithy-runtime-api",
@@ -180,9 +215,9 @@ dependencies = [
[[package]]
name = "aws-lc-sys"
version = "0.37.0"
version = "0.37.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c34dda4df7017c8db52132f0f8a2e0f8161649d15723ed63fc00c82d0f2081a"
checksum = "b092fe214090261288111db7a2b2c2118e5a7f30dc2569f1732c4069a6840549"
dependencies = [
"cc",
"cmake",
@@ -192,9 +227,9 @@ dependencies = [
[[package]]
name = "aws-runtime"
version = "1.6.0"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c635c2dc792cb4a11ce1a4f392a925340d1bdf499289b5ec1ec6810954eb43f5"
checksum = "b0f92058d22a46adf53ec57a6a96f34447daf02bff52e8fb956c66bcd5c6ac12"
dependencies = [
"aws-credential-types",
"aws-sigv4",
@@ -206,6 +241,7 @@ dependencies = [
"aws-smithy-types",
"aws-types",
"bytes 1.11.1",
"bytes-utils",
"fastrand",
"http 0.2.12",
"http 1.4.0",
@@ -219,9 +255,9 @@ dependencies = [
[[package]]
name = "aws-sdk-s3"
version = "1.122.0"
version = "1.123.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94c2ca0cba97e8e279eb6c0b2d0aa10db5959000e602ab2b7c02de6b85d4c19b"
checksum = "c018f22146966fdd493a664f62ee2483dff256b42a08c125ab6a084bde7b77fe"
dependencies = [
"aws-credential-types",
"aws-runtime",
@@ -254,9 +290,9 @@ dependencies = [
[[package]]
name = "aws-sdk-sso"
version = "1.93.0"
version = "1.94.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9dcb38bb33fc0a11f1ffc3e3e85669e0a11a37690b86f77e75306d8f369146a0"
checksum = "699da1961a289b23842d88fe2984c6ff68735fdf9bdcbc69ceaeb2491c9bf434"
dependencies = [
"aws-credential-types",
"aws-runtime",
@@ -278,9 +314,9 @@ dependencies = [
[[package]]
name = "aws-sdk-ssooidc"
version = "1.95.0"
version = "1.96.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ada8ffbea7bd1be1f53df1dadb0f8fdb04badb13185b3321b929d1ee3caad09"
checksum = "e3e3a4cb3b124833eafea9afd1a6cc5f8ddf3efefffc6651ef76a03cbc6b4981"
dependencies = [
"aws-credential-types",
"aws-runtime",
@@ -302,9 +338,9 @@ dependencies = [
[[package]]
name = "aws-sdk-sts"
version = "1.97.0"
version = "1.98.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6443ccadc777095d5ed13e21f5c364878c9f5bad4e35187a6cdbd863b0afcad"
checksum = "89c4f19655ab0856375e169865c91264de965bd74c407c7f1e403184b1049409"
dependencies = [
"aws-credential-types",
"aws-runtime",
@@ -327,9 +363,9 @@ dependencies = [
[[package]]
name = "aws-sigv4"
version = "1.3.8"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efa49f3c607b92daae0c078d48a4571f599f966dce3caee5f1ea55c4d9073f99"
checksum = "68f6ae9b71597dc5fd115d52849d7a5556ad9265885ad3492ea8d73b93bbc46e"
dependencies = [
"aws-credential-types",
"aws-smithy-eventstream",
@@ -355,9 +391,9 @@ dependencies = [
[[package]]
name = "aws-smithy-async"
version = "1.2.11"
version = "1.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52eec3db979d18cb807fc1070961cc51d87d069abe9ab57917769687368a8c6c"
checksum = "3cba48474f1d6807384d06fec085b909f5807e16653c5af5c45dfe89539f0b70"
dependencies = [
"futures-util",
"pin-project-lite",
@@ -366,9 +402,9 @@ dependencies = [
[[package]]
name = "aws-smithy-checksums"
version = "0.64.3"
version = "0.64.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddcf418858f9f3edd228acb8759d77394fed7531cce78d02bdda499025368439"
checksum = "a764fa7222922f6c0af8eea478b0ef1ba5ce1222af97e01f33ca5e957bd7f3b9"
dependencies = [
"aws-smithy-http",
"aws-smithy-types",
@@ -387,9 +423,9 @@ dependencies = [
[[package]]
name = "aws-smithy-eventstream"
version = "0.60.18"
version = "0.60.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35b9c7354a3b13c66f60fe4616d6d1969c9fd36b1b5333a5dfb3ee716b33c588"
checksum = "1c0b3e587fbaa5d7f7e870544508af8ce82ea47cd30376e69e1e37c4ac746f79"
dependencies = [
"aws-smithy-types",
"bytes 1.11.1",
@@ -398,9 +434,9 @@ dependencies = [
[[package]]
name = "aws-smithy-http"
version = "0.63.3"
version = "0.63.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "630e67f2a31094ffa51b210ae030855cb8f3b7ee1329bdd8d085aaf61e8b97fc"
checksum = "af4a8a5fe3e4ac7ee871237c340bbce13e982d37543b65700f4419e039f5d78e"
dependencies = [
"aws-smithy-eventstream",
"aws-smithy-runtime-api",
@@ -420,9 +456,9 @@ dependencies = [
[[package]]
name = "aws-smithy-http-client"
version = "1.1.9"
version = "1.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12fb0abf49ff0cab20fd31ac1215ed7ce0ea92286ba09e2854b42ba5cabe7525"
checksum = "0709f0083aa19b704132684bc26d3c868e06bd428ccc4373b0b55c3e8748a58b"
dependencies = [
"aws-smithy-async",
"aws-smithy-runtime-api",
@@ -450,27 +486,27 @@ dependencies = [
[[package]]
name = "aws-smithy-json"
version = "0.62.3"
version = "0.62.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cb96aa208d62ee94104645f7b2ecaf77bf27edf161590b6224bfbac2832f979"
checksum = "27b3a779093e18cad88bbae08dc4261e1d95018c4c5b9356a52bcae7c0b6e9bb"
dependencies = [
"aws-smithy-types",
]
[[package]]
name = "aws-smithy-observability"
version = "0.2.4"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0a46543fbc94621080b3cf553eb4cbbdc41dd9780a30c4756400f0139440a1d"
checksum = "4d3f39d5bb871aaf461d59144557f16d5927a5248a983a40654d9cf3b9ba183b"
dependencies = [
"aws-smithy-runtime-api",
]
[[package]]
name = "aws-smithy-query"
version = "0.60.13"
version = "0.60.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cebbddb6f3a5bd81553643e9c7daf3cc3dc5b0b5f398ac668630e8a84e6fff0"
checksum = "05f76a580e3d8f8961e5d48763214025a2af65c2fa4cd1fb7f270a0e107a71b0"
dependencies = [
"aws-smithy-types",
"urlencoding",
@@ -478,9 +514,9 @@ dependencies = [
[[package]]
name = "aws-smithy-runtime"
version = "1.10.0"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3df87c14f0127a0d77eb261c3bc45d5b4833e2a1f63583ebfb728e4852134ee"
checksum = "8fd3dfc18c1ce097cf81fced7192731e63809829c6cbf933c1ec47452d08e1aa"
dependencies = [
"aws-smithy-async",
"aws-smithy-http",
@@ -503,9 +539,9 @@ dependencies = [
[[package]]
name = "aws-smithy-runtime-api"
version = "1.11.3"
version = "1.11.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49952c52f7eebb72ce2a754d3866cc0f87b97d2a46146b79f80f3a93fb2b3716"
checksum = "8c55e0837e9b8526f49e0b9bfa9ee18ddee70e853f5bc09c5d11ebceddcb0fec"
dependencies = [
"aws-smithy-async",
"aws-smithy-types",
@@ -520,9 +556,9 @@ dependencies = [
[[package]]
name = "aws-smithy-types"
version = "1.4.3"
version = "1.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b3a26048eeab0ddeba4b4f9d51654c79af8c3b32357dc5f336cee85ab331c33"
checksum = "576b0d6991c9c32bc14fc340582ef148311f924d41815f641a308b5d11e8e7cd"
dependencies = [
"base64-simd",
"bytes 1.11.1",
@@ -546,18 +582,18 @@ dependencies = [
[[package]]
name = "aws-smithy-xml"
version = "0.60.13"
version = "0.60.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11b2f670422ff42bf7065031e72b45bc52a3508bd089f743ea90731ca2b6ea57"
checksum = "b53543b4b86ed43f051644f704a98c7291b3618b67adf057ee77a366fa52fcaa"
dependencies = [
"xmlparser",
]
[[package]]
name = "aws-types"
version = "1.3.11"
version = "1.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d980627d2dd7bfc32a3c025685a033eeab8d365cc840c631ef59d1b8f428164"
checksum = "6c50f3cdf47caa8d01f2be4a6663ea02418e892f9bbfd82c7b9a3a37eaccdd3a"
dependencies = [
"aws-credential-types",
"aws-smithy-async",
@@ -628,6 +664,15 @@ dependencies = [
"generic-array",
]
[[package]]
name = "block-buffer"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96eb4cdd6cf1b31d671e9efe75c5d1ec614776856cefbe109ca373554a6d514f"
dependencies = [
"hybrid-array",
]
[[package]]
name = "bson"
version = "2.15.0"
@@ -739,6 +784,17 @@ dependencies = [
"windows-link",
]
[[package]]
name = "cipher"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64727038c8c5e2bb503a15b9f5b9df50a1da9a33e83e1f93067d914f2c6604a5"
dependencies = [
"block-buffer 0.11.0",
"crypto-common 0.2.0",
"inout",
]
[[package]]
name = "cloudabi"
version = "0.0.3"
@@ -849,6 +905,12 @@ version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "cpubits"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ef0c543070d296ea414df2dd7625d1b24866ce206709d8a4a424f28377f5861"
[[package]]
name = "cpufeatures"
version = "0.2.17"
@@ -858,6 +920,15 @@ dependencies = [
"libc",
]
[[package]]
name = "cpufeatures"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
dependencies = [
"libc",
]
[[package]]
name = "crc"
version = "3.3.0"
@@ -1013,14 +1084,34 @@ dependencies = [
[[package]]
name = "crypto-common"
version = "0.1.7"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "crypto-common"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "211f05e03c7d03754740fd9e585de910a095d6b99f8bcfffdef8319fa02a8331"
dependencies = [
"getrandom 0.4.1",
"hybrid-array",
"rand_core 0.10.0",
]
[[package]]
name = "ctr"
version = "0.10.0-rc.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "65ea71550d18331d179854662ab330bb54306b9b56020d0466aae2a58f4e17c1"
dependencies = [
"cipher",
]
[[package]]
name = "darling"
version = "0.21.3"
@@ -1074,9 +1165,9 @@ dependencies = [
[[package]]
name = "deranged"
version = "0.5.5"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587"
checksum = "cc3dc5ad92c2e2d1c193bbbbdf2ea477cb81331de4f3103f267ca18368b988c4"
dependencies = [
"powerfmt",
]
@@ -1132,8 +1223,8 @@ version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
"block-buffer 0.10.4",
"crypto-common 0.1.6",
"subtle",
]
@@ -1290,6 +1381,12 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "foldhash"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "foldhash"
version = "0.2.0"
@@ -1445,9 +1542,9 @@ dependencies = [
[[package]]
name = "generic-array"
version = "0.14.7"
version = "0.14.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2"
dependencies = [
"typenum",
"version_check",
@@ -1480,6 +1577,29 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "getrandom"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec"
dependencies = [
"cfg-if 1.0.4",
"libc",
"r-efi",
"rand_core 0.10.0",
"wasip2",
"wasip3",
]
[[package]]
name = "ghash"
version = "0.6.0-rc.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb9be1ab8718f9d16384cb3626a5a7d7eac4d3fd1b2564e97592f40523dc0228"
dependencies = [
"polyval",
]
[[package]]
name = "group"
version = "0.12.1"
@@ -1529,6 +1649,15 @@ dependencies = [
"tracing",
]
[[package]]
name = "hashbrown"
version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
dependencies = [
"foldhash 0.1.5",
]
[[package]]
name = "hashbrown"
version = "0.16.1"
@@ -1537,7 +1666,7 @@ checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
dependencies = [
"allocator-api2",
"equivalent",
"foldhash",
"foldhash 0.2.0",
]
[[package]]
@@ -1680,6 +1809,15 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]]
name = "hybrid-array"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1b229d73f5803b562cc26e4da0396c8610a4ee209f4fac8fa4f8d709166dc45"
dependencies = [
"typenum",
]
[[package]]
name = "hyper"
version = "0.14.32"
@@ -1889,6 +2027,12 @@ dependencies = [
"zerovec",
]
[[package]]
name = "id-arena"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
[[package]]
name = "ident_case"
version = "1.0.1"
@@ -1934,7 +2078,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
dependencies = [
"equivalent",
"hashbrown",
"hashbrown 0.16.1",
"serde",
"serde_core",
]
[[package]]
name = "inout"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4250ce6452e92010fdf7268ccc5d14faa80bb12fc741938534c58f16804e03c7"
dependencies = [
"hybrid-array",
]
[[package]]
@@ -2039,10 +2194,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
version = "0.2.180"
name = "leb128fmt"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc"
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
[[package]]
name = "libc"
version = "0.2.181"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "459427e2af2b9c839b132acb702a1c654d95e10f8c326bfc2ad11310e458b1c5"
[[package]]
name = "libredox"
@@ -2052,7 +2213,7 @@ checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616"
dependencies = [
"bitflags 2.10.0",
"libc",
"redox_syscall 0.7.0",
"redox_syscall 0.7.1",
]
[[package]]
@@ -2097,7 +2258,7 @@ version = "0.16.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593"
dependencies = [
"hashbrown",
"hashbrown 0.16.1",
]
[[package]]
@@ -2471,6 +2632,24 @@ dependencies = [
"url 2.5.8",
]
[[package]]
name = "objc2-core-foundation"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536"
dependencies = [
"bitflags 2.10.0",
]
[[package]]
name = "objc2-system-configuration"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7216bd11cbda54ccabcab84d523dc93b858ec75ecfb3a7d89513fa22464da396"
dependencies = [
"objc2-core-foundation",
]
[[package]]
name = "once_cell"
version = "1.21.3"
@@ -2660,9 +2839,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
[[package]]
name = "portabase-agent"
version = "1.1.0-rc.1"
name = "polyval"
version = "0.7.0-rc.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e83fbff7a079b2d37c70aa6bd5eedb9e5d09ceb9b4ecd31e9ea212d00e9b0bc"
dependencies = [
"cpubits",
"cpufeatures 0.3.0",
"universal-hash",
]
[[package]]
name = "portabase-agent"
version = "1.1.2-rc.1"
dependencies = [
"aes",
"aes-gcm",
"anyhow",
"async-compression",
"async-http-client",
@@ -2677,6 +2869,8 @@ dependencies = [
"dotenvy",
"flate2",
"futures 0.3.31",
"futures-util",
"generic-array",
"hex",
"hyper 1.8.1",
"log",
@@ -2695,12 +2889,14 @@ dependencies = [
"time",
"tokio 1.49.0",
"tokio-postgres",
"tokio-stream",
"tokio-tar",
"tokio-util",
"toml",
"tracing",
"tracing-appender",
"tracing-subscriber",
"typenum",
"uuid",
]
@@ -2763,6 +2959,16 @@ dependencies = [
"zerocopy",
]
[[package]]
name = "prettyplease"
version = "0.2.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
dependencies = [
"proc-macro2",
"syn",
]
[[package]]
name = "proc-macro2"
version = "1.0.106"
@@ -2908,6 +3114,12 @@ dependencies = [
"getrandom 0.3.4",
]
[[package]]
name = "rand_core"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba"
[[package]]
name = "redis"
version = "1.0.3"
@@ -2958,9 +3170,9 @@ dependencies = [
[[package]]
name = "redox_syscall"
version = "0.7.0"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f3fe0889e69e2ae9e41f4d6c4c0181701d00e4697b356fb1f74173a5e0ee27"
checksum = "35985aa610addc02e24fc232012c86fd11f14111180f902b67e2d5331f8ebf2b"
dependencies = [
"bitflags 2.10.0",
]
@@ -3257,9 +3469,9 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "ryu"
version = "1.0.22"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984"
checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
[[package]]
name = "same-file"
@@ -3474,7 +3686,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
dependencies = [
"cfg-if 1.0.4",
"cpufeatures",
"cpufeatures 0.2.17",
"digest",
]
@@ -3491,7 +3703,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
dependencies = [
"cfg-if 1.0.4",
"cpufeatures",
"cpufeatures 0.2.17",
"digest",
]
@@ -3630,9 +3842,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "syn"
version = "2.0.114"
version = "2.0.115"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a"
checksum = "6e614ed320ac28113fa64972c4262d5dbc89deacdfd00c34a3e4cea073243c12"
dependencies = [
"proc-macro2",
"quote",
@@ -3711,12 +3923,12 @@ dependencies = [
[[package]]
name = "tempfile"
version = "3.24.0"
version = "3.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c"
checksum = "0136791f7c95b1f6dd99f9cc786b91bb81c3800b639b3478e561ddb7be95e5f1"
dependencies = [
"fastrand",
"getrandom 0.3.4",
"getrandom 0.4.1",
"once_cell",
"rustix",
"windows-sys 0.61.2",
@@ -4154,9 +4366,9 @@ dependencies = [
[[package]]
name = "toml"
version = "0.9.11+spec-1.1.0"
version = "0.9.12+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3afc9a848309fe1aaffaed6e1546a7a14de1f935dc9d89d32afd9a44bab7c46"
checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863"
dependencies = [
"indexmap",
"serde_core",
@@ -4178,9 +4390,9 @@ dependencies = [
[[package]]
name = "toml_parser"
version = "1.0.6+spec-1.1.0"
version = "1.0.8+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44"
checksum = "0742ff5ff03ea7e67c8ae6c93cac239e0d9784833362da3f9a9c1da8dfefcbdc"
dependencies = [
"winnow 0.7.14",
]
@@ -4357,9 +4569,9 @@ checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5"
[[package]]
name = "unicode-ident"
version = "1.0.22"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
checksum = "537dd038a89878be9b64dd4bd1b260315c1bb94f4d784956b81e27a088d9a09e"
[[package]]
name = "unicode-normalization"
@@ -4388,6 +4600,16 @@ version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
name = "universal-hash"
version = "0.6.0-rc.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "058482a494bb3c9c39447d8b40a3a0f38ebb3dccaf02c5a2d681e69035f8da11"
dependencies = [
"crypto-common 0.2.0",
"subtle",
]
[[package]]
name = "untrusted"
version = "0.9.0"
@@ -4509,6 +4731,15 @@ dependencies = [
"wit-bindgen",
]
[[package]]
name = "wasip3"
version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
dependencies = [
"wit-bindgen",
]
[[package]]
name = "wasite"
version = "1.0.2"
@@ -4577,6 +4808,28 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "wasm-encoder"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
dependencies = [
"leb128fmt",
"wasmparser",
]
[[package]]
name = "wasm-metadata"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
dependencies = [
"anyhow",
"indexmap",
"wasm-encoder",
"wasmparser",
]
[[package]]
name = "wasm-streams"
version = "0.5.0"
@@ -4590,6 +4843,18 @@ dependencies = [
"web-sys",
]
[[package]]
name = "wasmparser"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
dependencies = [
"bitflags 2.10.0",
"hashbrown 0.15.5",
"indexmap",
"semver 1.0.27",
]
[[package]]
name = "web-sys"
version = "0.3.85"
@@ -4630,11 +4895,13 @@ dependencies = [
[[package]]
name = "whoami"
version = "2.1.0"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fae98cf96deed1b7572272dfc777713c249ae40aa1cf8862e091e8b745f5361"
checksum = "d6a5b12f9df4f978d2cfdb1bd3bac52433f44393342d7ee9c25f5a1c14c0f45d"
dependencies = [
"libc",
"libredox",
"objc2-system-configuration",
"wasite",
"web-sys",
]
@@ -5076,6 +5343,88 @@ name = "wit-bindgen"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
dependencies = [
"wit-bindgen-rust-macro",
]
[[package]]
name = "wit-bindgen-core"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
dependencies = [
"anyhow",
"heck",
"wit-parser",
]
[[package]]
name = "wit-bindgen-rust"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
dependencies = [
"anyhow",
"heck",
"indexmap",
"prettyplease",
"syn",
"wasm-metadata",
"wit-bindgen-core",
"wit-component",
]
[[package]]
name = "wit-bindgen-rust-macro"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
dependencies = [
"anyhow",
"prettyplease",
"proc-macro2",
"quote",
"syn",
"wit-bindgen-core",
"wit-bindgen-rust",
]
[[package]]
name = "wit-component"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
dependencies = [
"anyhow",
"bitflags 2.10.0",
"indexmap",
"log",
"serde",
"serde_derive",
"serde_json",
"wasm-encoder",
"wasm-metadata",
"wasmparser",
"wit-parser",
]
[[package]]
name = "wit-parser"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
dependencies = [
"anyhow",
"id-arena",
"indexmap",
"log",
"semver 1.0.27",
"serde",
"serde_derive",
"serde_json",
"unicode-xid",
"wasmparser",
]
[[package]]
name = "writeable"
@@ -5229,6 +5578,6 @@ dependencies = [
[[package]]
name = "zmij"
version = "1.0.19"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ff05f8caa9038894637571ae6b9e29466c1f4f829d26c9b28f869a29cbe3445"
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
+7 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "portabase-agent"
version = "1.1.0-rc.1"
version = "1.1.2-rc.2"
edition = "2024"
[dependencies]
@@ -43,6 +43,12 @@ tokio-tar = "0.3.1"
oauth2 = "5.0.0"
hyper = "1.8.1"
async-http-client = "0.2.0"
aes-gcm = "0.11.0-rc.3"
generic-array = "0.14.7"
futures-util = "0.3.31"
tokio-stream = "0.1.18"
aes = "0.9.0-rc.4"
typenum = "1.19.0"
[[bin]]
+3 -4
View File
@@ -11,13 +11,12 @@ services:
# - ./databases.toml:/config/config.toml
- cargo-registry:/usr/local/cargo/registry
- cargo-git:/usr/local/cargo/git
- cargo-target:/app/target
# - cargo-target:/app/target
environment:
APP_ENV: development
LOG: debug
TZ: "Europe/Paris"
EDGE_KEY: "eyJzZXJ2ZXJVcmwiOiJodHRwOi8vbG9jYWxob3N0Ojg4ODciLCJhZ2VudElkIjoiOTI0OGU1ZWUtYWU5Yi00ZGQ0LTk3MDUtYjEwYWQzNDU4YmNjIiwicHVibGljS2V5IjoiLS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tXG5NSUlCQ2dLQ0FRRUE5TWV4M2pmdnVLdFB5YU1ERnh2Ulp2dmd3YkRJQ2JzQi81Wll5NDNSVVRBaXZRYjJiSDdYXG5qRHBQd1lJeCs4UFBrbHlRbDVMQzV1UWZEaCs4SVd4OG1LZ3FvMXpWMkdiZXdGbEdEWFYxVEdyU1ZEU25aSWR4XG52bWdYc29EeXhVMlJvWUFUMS9YMWxuc2YxenZKdkFMTkhXdEhRdk42SjVDZTFSMmFsendVRGFEVXlJNzRmSldQXG5tNTh0SDMrYklXL0VVTXdjaWNxM0oySWw3Vm9KNkZNUHJQL1ZSOWEvdFF1SU1qa200MXpFY2NscExPa2luRkxuXG54NmVUWkFSZUpya2UrbnRvZ2t4TGEyRWV5a1lUNzB4V3hKNWp5ZExBVnRvNkkyQlVLVVJoTkowTUFaU29NYUtvXG5iMGJRcnY1UzExZWllMnMrT2I3aTYzSFpkVUx0UmV1MVJ3SURBUUFCXG4tLS0tLUVORCBSU0EgUFVCTElDIEtFWS0tLS0tXG4ifQ"
# EDGE_KEY: "eyJzZXJ2ZXJVcmwiOiJodHRwOi8vbG9jYWxob3N0Ojg4ODciLCJhZ2VudElkIjoiZWE2NTg1MDctZTA5My00NDUxLWIxZDAtMDgwZWZjMGNmNWYzIiwicHVibGljS2V5IjoiLS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tXG5NSUlCQ2dLQ0FRRUE5TWV4M2pmdnVLdFB5YU1ERnh2Ulp2dmd3YkRJQ2JzQi81Wll5NDNSVVRBaXZRYjJiSDdYXG5qRHBQd1lJeCs4UFBrbHlRbDVMQzV1UWZEaCs4SVd4OG1LZ3FvMXpWMkdiZXdGbEdEWFYxVEdyU1ZEU25aSWR4XG52bWdYc29EeXhVMlJvWUFUMS9YMWxuc2YxenZKdkFMTkhXdEhRdk42SjVDZTFSMmFsendVRGFEVXlJNzRmSldQXG5tNTh0SDMrYklXL0VVTXdjaWNxM0oySWw3Vm9KNkZNUHJQL1ZSOWEvdFF1SU1qa200MXpFY2NscExPa2luRkxuXG54NmVUWkFSZUpya2UrbnRvZ2t4TGEyRWV5a1lUNzB4V3hKNWp5ZExBVnRvNkkyQlVLVVJoTkowTUFaU29NYUtvXG5iMGJRcnY1UzExZWllMnMrT2I3aTYzSFpkVUx0UmV1MVJ3SURBUUFCXG4tLS0tLUVORCBSU0EgUFVCTElDIEtFWS0tLS0tXG4ifQ"
EDGE_KEY: "eyJzZXJ2ZXJVcmwiOiJodHRwOi8vbG9jYWxob3N0Ojg4ODciLCJhZ2VudElkIjoiOWZjYjhmNjUtNTcyMC00N2M3LThkNWItYWFlMmVjOTc2OWU1IiwibWFzdGVyS2V5QjY0IjoiQlhWM1hvbEM2NTZTVjdkTmdjV1BHUWxrKytycExJNmxHRGk3Q1BCNWllbz0ifQ=="
#POOLING: 1
#DATABASES_CONFIG_FILE: "config.toml"
extra_hosts:
@@ -95,7 +94,7 @@ services:
volumes:
cargo-registry:
cargo-git:
cargo-target:
# cargo-target:
postgres-data:
BIN
View File
Binary file not shown.
+1 -1
View File
@@ -35,7 +35,7 @@ impl Context {
panic!("Cannot initialize AgentContext due to invalid EDGE_KEY");
}
};
let server_url = format!("{}/api", edge_key.server_url);
let api_client = ApiClient::new(server_url);
+15 -8
View File
@@ -1,19 +1,26 @@
use crate::services::config::DatabaseConfig;
use anyhow::Context;
use std::collections::HashMap;
use std::process::Command;
use tokio::process::Command;
use tokio::time::{Duration, timeout};
pub async fn run(cfg: DatabaseConfig, env: HashMap<String, String>) -> anyhow::Result<bool> {
let output = Command::new("mysqladmin")
.arg("--host")
let mut cmd = Command::new("mysqladmin");
cmd.arg("--host")
.arg(cfg.host)
.arg("--port")
.arg(cfg.port.to_string())
.arg("--user")
.arg(cfg.username)
.arg("ping")
.envs(env)
.output()
.with_context(|| format!("Failed to ping MySQL server {}", cfg.name))?;
Ok(output.status.success())
.envs(env);
let result = timeout(Duration::from_secs(10), cmd.output()).await;
match result {
Ok(output) => {
let output = output?;
Ok(output.status.success())
}
Err(_) => Ok(false),
}
}
+47 -21
View File
@@ -69,7 +69,6 @@ impl BackupService {
return;
}
Ok(false) => {
match ctx
.api
.backup_create(
@@ -85,13 +84,27 @@ impl BackupService {
info!("Created temp directory {}", tmp_path.display());
match BackupService::run(db_cfg, &tmp_path).await {
Ok(mut result) => {
let backup_id = backup_created_result.unwrap().backup.id;
if result.status == "failed" {
error!("Backup failed early for {}", result.generated_id);
let service = BackupService { ctx: ctx.clone() };
let _ = service
.send_result(result, vec![], &backup_id)
.await;
return;
}
if let Some(backup_file) = result.backup_file.take() {
match compress_to_tar_gz_large(&backup_file).await {
Ok(compression_result) => {
result.backup_file =
Some(compression_result.compressed_path);
let service = BackupService { ctx: ctx.clone() };
let backup_id = backup_created_result.unwrap().backup.id;
match service
.upload(
result.clone(),
@@ -107,7 +120,7 @@ impl BackupService {
.send_result(
result,
upload_result,
&backup_id
&backup_id,
)
.await
{
@@ -147,7 +160,7 @@ impl BackupService {
}
Err(e) => error!("Backup creation failed: {}", e),
}
},
}
Err(e) => error!("An error occurred while checking lock : {}", e),
}
}
@@ -162,7 +175,16 @@ impl BackupService {
let generated_id = cfg.generated_id.clone();
let db_type = cfg.db_type.clone();
let reachable = db_instance.ping().await.unwrap_or(false);
let reachable = match db_instance.ping().await {
Ok(v) => v,
Err(e) => {
error!("Ping failed: {}", e);
return Err(e.into());
}
};
info!("Reachable: {}", reachable);
if !reachable {
return Ok(BackupResult {
@@ -277,8 +299,6 @@ impl BackupService {
}
};
match self.ctx.api.backup_upload_status(
self.ctx.edge_key.agent_id.clone(),
generated_id.clone(),
@@ -286,12 +306,12 @@ impl BackupService {
status,
remote_path,
total_size,
backup_id
backup_id,
).await {
Ok(_) => {
upload_result
},
Err(err)=> {
}
Err(err) => {
error!(
"backup_upload_status failed (generated_id={}, storage_id={}): {}",
generated_id, storage_id, err
@@ -357,17 +377,23 @@ impl BackupService {
"failed"
};
let file_size = upload_results
.iter()
.map(|r| r.total_size)
.try_fold((0u64, 0u64), |(sum, count), v| {
match v {
Some(size) => Ok((sum + size, count + 1)),
None => Err(()), // stop and return None
}
})
.ok()
.map(|(sum, count)| sum / count);
let file_size = if status == "failed" {
None
} else {
let mut sum = 0u64;
let mut count = 0u64;
for size in upload_results.iter().filter_map(|r| r.total_size) {
sum += size;
count += 1;
}
if count == 0 {
None
} else {
Some(sum / count)
}
};
match self
.ctx
+104 -27
View File
@@ -1,16 +1,18 @@
#![allow(dead_code)]
#![warn(unused_assignments)]
use crate::core::context::Context;
use crate::domain::factory::DatabaseFactory;
use crate::services::api::models::agent::status::DatabaseStatus;
use crate::services::config::{DatabaseConfig, DatabasesConfig};
use crate::utils::compress::decompress_large_tar_gz;
use crate::utils::file::decrypt_file_stream_gcm;
use anyhow::Result;
use reqwest::{Client, Url};
use serde::Serialize;
use std::path::Path;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use tempfile::TempDir;
use tracing::{error, info};
use crate::services::api::models::agent::status::DatabaseStatus;
use crate::utils::compress::decompress_large_tar_gz;
#[derive(Debug, Serialize)]
pub struct RestoreResult {
@@ -46,7 +48,14 @@ impl RestoreService {
Ok(temp_dir) => {
let tmp_path = temp_dir.path().to_path_buf();
info!("Created temp directory {}", tmp_path.display());
match RestoreService::run(db_cfg, &tmp_path, &file_to_restore.unwrap()).await {
match RestoreService::run(
&ctx_clone,
db_cfg,
&tmp_path,
&file_to_restore.unwrap(),
)
.await
{
Ok(result) => {
let service = RestoreService { ctx: ctx_clone };
service.send_result(result).await;
@@ -62,6 +71,7 @@ impl RestoreService {
}
pub async fn run(
ctx: &Arc<Context>,
cfg: DatabaseConfig,
tmp_path: &Path,
file_url: &str,
@@ -70,8 +80,9 @@ impl RestoreService {
info!("File url: {}", file_url);
let client = reqwest::Client::new();
let client = Client::new();
let response = client.get(file_url).send().await?;
if !response.status().is_success() {
error!("Backup download failed with status {}", response.status());
return Ok(RestoreResult {
@@ -80,30 +91,96 @@ impl RestoreService {
});
}
let filename_from_header = response
.headers()
.get(reqwest::header::CONTENT_DISPOSITION)
.and_then(|v| v.to_str().ok())
.and_then(|s| s.split("filename=").nth(1))
.map(|f| f.trim_matches('"').to_string());
let filename_from_url = Url::parse(file_url).ok().and_then(|u| {
u.path_segments()?
.last()
.filter(|s| !s.is_empty())
.map(|s| s.to_string())
});
let filename = filename_from_header
.or(filename_from_url)
.unwrap_or_else(|| "downloaded_file".to_string());
info!("File name: {}", filename);
let bytes = response.bytes().await?;
let compressed_archive = tmp_path.join("compressed_archive_tmp.tar.gz");
tokio::fs::write(&compressed_archive, &bytes).await?;
info!("Backup downloaded to {}", compressed_archive.display());
let decompressed_files = decompress_large_tar_gz(compressed_archive.as_path(), tmp_path).await?;
info!("Decompressed_files {:#?}", decompressed_files);
if decompressed_files.is_empty() {
return Ok(RestoreResult {
generated_id,
status: "failed".into(),
});
}
let backup_file_path = if decompressed_files.len() == 1 {
decompressed_files.get(0).unwrap()
}else {
compressed_archive.as_path()
let is_legacy_file = if filename.ends_with(".sql") {
true
} else if filename.ends_with(".dump") {
true
} else {
false
};
let downloaded_file = tmp_path.join(&filename);
tokio::fs::write(&downloaded_file, &bytes).await?;
info!("Backup downloaded to {}", downloaded_file.display());
info!("Decompressed file {}", backup_file_path.display());
let backup_file_path: PathBuf = if !is_legacy_file {
let encrypted = if filename.ends_with(".tar.gz") {
false
} else if filename.ends_with(".tar.gz.enc") {
true
} else {
return Ok(RestoreResult {
generated_id,
status: "failed".into(),
});
};
info!("Encrypted: {}", encrypted);
let mut compressed_archive = downloaded_file.clone();
if encrypted {
let new_name = downloaded_file
.file_name()
.and_then(|n| n.to_str())
.and_then(|n| n.strip_suffix(".enc"))
.ok_or_else(|| anyhow::anyhow!("Invalid encrypted filename"))?;
let new_compressed_archive = tmp_path.join(new_name);
decrypt_file_stream_gcm(
downloaded_file,
new_compressed_archive.clone(),
ctx.edge_key.master_key_b64.clone(),
)
.await
.map_err(|e| {
error!("Failed to decrypt file: {}", e);
e
})?;
compressed_archive = new_compressed_archive;
}
let decompressed_files =
decompress_large_tar_gz(compressed_archive.as_path(), tmp_path).await?;
if decompressed_files.is_empty() {
return Ok(RestoreResult {
generated_id,
status: "failed".into(),
});
}
if decompressed_files.len() == 1 {
decompressed_files[0].clone()
} else {
compressed_archive
}
} else {
downloaded_file.clone()
};
let db_instance = DatabaseFactory::create_for_restore(cfg.clone(), &backup_file_path).await;
let reachable = db_instance.ping().await.unwrap_or(false);
@@ -121,7 +198,7 @@ impl RestoreService {
status: "success".into(),
}),
Err(e) => {
log::error!("Restore failed: {:?}", e);
error!("Restore failed: {:?}", e);
Ok(RestoreResult {
generated_id,
status: "failed".into(),
@@ -56,7 +56,7 @@ impl StorageProvider for GoogleDriveProvider {
let upload = match build_stream(
&file_path,
encrypt,
encrypt.then(|| ctx.edge_key.public_key.as_bytes().to_vec()),
&ctx.edge_key.master_key_b64
)
.await
{
+2 -27
View File
@@ -7,8 +7,6 @@ use crate::utils::file::{full_file_name, full_file_path};
use crate::utils::stream::build_stream;
use crate::utils::tus::upload_to_tus_stream_with_headers;
use async_trait::async_trait;
use base64::Engine;
use base64::engine::general_purpose;
use reqwest::header::{HeaderMap, HeaderValue};
use std::sync::Arc;
use tokio::fs;
@@ -58,7 +56,7 @@ impl StorageProvider for LocalProvider {
let upload = match build_stream(
&file_path,
encrypt,
encrypt.then(|| ctx.edge_key.public_key.as_bytes().to_vec()),
&ctx.edge_key.master_key_b64
)
.await
{
@@ -92,30 +90,7 @@ impl StorageProvider for LocalProvider {
"X-Method",
HeaderValue::from_str(&method.to_string()).unwrap(),
);
if let Some(enc) = upload.encryption {
let mut meta_pairs = Vec::new();
meta_pairs.push(format!("version {}", "1"));
meta_pairs.push(format!("cipher {}", "AES-256-CBC+RSA-OAEP-SHA256"));
meta_pairs.push(format!(
"encrypted_aes_key_b64 {}",
general_purpose::STANDARD.encode(&enc.encrypted_aes_key)
));
meta_pairs.push(format!(
"iv_b64 {}",
general_purpose::STANDARD.encode(&enc.iv)
));
let metadata_header_value = meta_pairs.join(",");
extra_headers.insert(
"Upload-Metadata",
HeaderValue::from_str(&*general_purpose::STANDARD.encode(metadata_header_value))
.unwrap(),
);
}
let tus_endpoint = format!("{}/tus/files", ctx.edge_key.server_url);
match upload_to_tus_stream_with_headers(upload.stream, &tus_endpoint, extra_headers, total_size).await {
+3 -31
View File
@@ -6,7 +6,7 @@ use crate::services::backup::{BackupResult, UploadResult};
use crate::services::storage::StorageProvider;
use crate::services::storage::providers::s3::models::S3ProviderConfig;
use crate::utils::common::BackupMethod;
use crate::utils::file::{EncryptionMetadataFile, full_file_name, full_file_path};
use crate::utils::file::{full_file_name, full_file_path};
use crate::utils::stream::build_stream;
use async_trait::async_trait;
use aws_sdk_s3 as s3;
@@ -14,7 +14,6 @@ use aws_sdk_s3::config::BehaviorVersion;
use aws_sdk_s3::config::Region;
use aws_sdk_s3::primitives::ByteStream;
use aws_sdk_s3::types::{CompletedMultipartUpload, CompletedPart};
use base64::{Engine as _, engine::general_purpose};
use futures::StreamExt;
use std::pin::Pin;
use std::sync::Arc;
@@ -62,7 +61,7 @@ impl StorageProvider for S3Provider {
let upload = match build_stream(
&file_path,
encrypt,
encrypt.then(|| ctx.edge_key.public_key.as_bytes().to_vec()),
&ctx.edge_key.master_key_b64,
)
.await
{
@@ -100,7 +99,7 @@ impl StorageProvider for S3Provider {
"static-creds",
);
let region = Region::new(config.region.clone().unwrap_or("eu-central-3".to_string()));
let region = Region::new(config.region.clone().unwrap_or("us-east-1".to_string()));
let sdk_config = s3::config::Builder::new()
.credentials_provider(credentials)
@@ -284,33 +283,6 @@ impl StorageProvider for S3Provider {
remote_file_path
);
if let Some(enc) = upload.encryption {
let meta = EncryptionMetadataFile {
version: 1,
cipher: "AES-256-CBC+RSA-OAEP-SHA256".to_string(),
encrypted_aes_key_b64: general_purpose::STANDARD
.encode(enc.encrypted_aes_key),
iv_b64: general_purpose::STANDARD.encode(enc.iv),
};
let meta_toml = toml::to_string(&meta).expect("Serialization error");
let meta_key = format!("{}.meta", remote_file_path);
client
.put_object()
.bucket(bucket)
.key(&meta_key)
.body(ByteStream::from(meta_toml.into_bytes()))
.content_type("application/toml")
.send()
.await
.map_err(|e| {
error!("Metadata upload failed: {}", e);
e
})
.unwrap();
}
UploadResult {
storage_id: storage.id.clone(),
@@ -9,5 +9,3 @@ pub struct S3ProviderConfig {
pub ssl: bool,
pub region: Option<String>,
}
+4 -5
View File
@@ -2,7 +2,6 @@ use base64::{Engine as _, engine::general_purpose};
use serde::Deserialize;
use serde_json::Value;
use thiserror::Error;
use tracing::error;
#[derive(Debug, Deserialize)]
#[allow(dead_code)]
@@ -11,8 +10,8 @@ pub struct EdgeKey {
pub server_url: String,
#[serde(rename = "agentId")]
pub agent_id: String,
#[serde(rename = "publicKey")]
pub public_key: String,
#[serde(rename = "masterKeyB64")]
pub master_key_b64: String,
}
#[derive(Debug, Error)]
@@ -35,14 +34,14 @@ pub fn decode_edge_key(edge_key: &str) -> Result<EdgeKey, EdgeKeyError> {
let decoded_str = String::from_utf8_lossy(&decoded_bytes);
let parsed: Value = serde_json::from_str(&decoded_str)?;
if parsed.get("serverUrl").is_some()
&& parsed.get("agentId").is_some()
&& parsed.get("publicKey").is_some()
&& parsed.get("masterKeyB64").is_some()
{
let edge_key: EdgeKey = serde_json::from_value(parsed)?;
Ok(edge_key)
} else {
error!("EDGE_KEY INVALID");
Err(EdgeKeyError::InvalidKey)
}
}
+123 -48
View File
@@ -1,21 +1,26 @@
#![allow(dead_code)]
use anyhow::Result;
use async_stream::try_stream;
use bytes::Bytes;
use chrono::Utc;
use futures::Stream;
use openssl::encrypt::Encrypter;
use openssl::hash::MessageDigest;
use openssl::pkey::PKey;
use openssl::rsa::Padding;
use openssl::symm::{Cipher, Crypter, Mode};
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
use tokio::fs::File;
use tokio::io::{AsyncReadExt, BufReader};
use tokio::io::{AsyncBufReadExt, AsyncReadExt, BufReader};
use uuid::Uuid;
use aes_gcm::aead::{Aead, KeyInit};
use aes_gcm::{Aes256Gcm, Key, Nonce};
use base64::Engine;
use base64::engine::general_purpose;
use bytes::Bytes;
use futures::Stream;
use rand::rngs::OsRng;
use rand::TryRngCore;
use tokio::io::{AsyncWriteExt, BufWriter};
use tokio::sync::mpsc;
use tokio_stream::wrappers::ReceiverStream;
use tracing::info;
#[derive(Serialize, Deserialize)]
pub struct EncryptionMetadataFile {
pub version: u8,
@@ -32,7 +37,7 @@ pub fn full_extension(path: &Path) -> String {
.to_string()
}
pub fn full_file_name( encrypt: bool) -> String {
pub fn full_file_name(encrypt: bool) -> String {
let uuid = Uuid::new_v4();
let base_name = format!("{}.{}", uuid, "tar.gz");
if encrypt {
@@ -46,55 +51,125 @@ pub fn full_file_path(file_name: &String) -> String {
format!("backups/{}/{}", Utc::now().format("%Y-%m-%d"), file_name)
}
pub async fn encrypt_file_stream(
const CHUNK_SIZE: usize = 16 * 1024 * 1024;
#[derive(Serialize, Deserialize, Debug)]
struct FileHeader {
version: u8,
cipher: String,
chunk_size: usize,
base_nonce: Vec<u8>,
}
pub async fn encrypt_file_stream_gcm(
file_path: PathBuf,
aes_key: [u8; 32],
iv: [u8; 16],
pub_key_pem: Vec<u8>,
) -> Result<(impl Stream<Item = Result<Bytes>> + Send + 'static, Vec<u8>)> {
// ---------- Encrypt AES key with RSA ----------
let pkey = PKey::public_key_from_pem(&pub_key_pem)?;
let mut rsa = Encrypter::new(&pkey)?;
rsa.set_rsa_padding(Padding::PKCS1_OAEP)?;
rsa.set_rsa_oaep_md(MessageDigest::sha256())?;
rsa.set_rsa_mgf1_md(MessageDigest::sha256())?;
master_key_b64: String,
) -> Result<impl Stream<Item = Result<Bytes>> + Send + 'static> {
let master_key_bytes = general_purpose::STANDARD
.decode(master_key_b64)
.map_err(|_| anyhow::anyhow!("Invalid base64"))?;
let mut encrypted_key = vec![0u8; rsa.encrypt_len(&aes_key)?];
let len = rsa.encrypt(&aes_key, &mut encrypted_key)?;
encrypted_key.truncate(len);
let (tx, rx) = mpsc::channel(8);
// ---------- Streaming AES encryption ----------
let stream = try_stream! {
let file = File::open(&file_path).await?;
tokio::spawn(async move {
let mut rng = OsRng;
let mut base_nonce = [0u8; 8];
rng.try_fill_bytes(&mut base_nonce).unwrap();
let key = Key::<Aes256Gcm>::try_from(master_key_bytes.as_slice())
.map_err(|_| anyhow::anyhow!("Invalid AES-256 key length")).unwrap();
let cipher = Aes256Gcm::new(&key);
let header = FileHeader {
version: 1,
cipher: "AES-256-GCM".to_string(),
chunk_size: CHUNK_SIZE,
base_nonce: base_nonce.to_vec(),
};
let header_json = serde_json::to_string(&header).unwrap();
tx.send(Ok(Bytes::from(header_json + "\n"))).await.unwrap();
let file = File::open(&file_path).await.unwrap();
let mut reader = BufReader::new(file);
let cipher = Cipher::aes_256_cbc();
let mut crypter = Crypter::new(cipher, Mode::Encrypt, &aes_key, Some(&iv))?;
crypter.pad(true);
let mut buffer = vec![0u8; 1024 * 1024];
let mut buffer = vec![0u8; CHUNK_SIZE];
let mut chunk_index: u32 = 0;
loop {
let n = reader.read(&mut buffer).await?;
let n = reader.read(&mut buffer).await.unwrap();
if n == 0 {
break;
}
let mut out = vec![0u8; n + cipher.block_size()];
let count = crypter.update(&buffer[..n], &mut out)?;
out.truncate(count);
let mut nonce_bytes = [0u8; 12];
nonce_bytes[..8].copy_from_slice(&base_nonce);
nonce_bytes[8..].copy_from_slice(&chunk_index.to_be_bytes());
let nonce = Nonce::try_from(&nonce_bytes[..])
.map_err(|_| anyhow::anyhow!("Invalid nonce length")).unwrap();
yield Bytes::from(out);
let ciphertext = cipher.encrypt(&nonce, &buffer[..n]).unwrap();
let mut out = Vec::with_capacity(4 + ciphertext.len());
out.extend_from_slice(&(ciphertext.len() as u32).to_be_bytes());
out.extend_from_slice(&ciphertext);
tx.send(Ok(Bytes::from(out))).await.unwrap();
chunk_index += 1;
}
});
let mut final_block = vec![0u8; cipher.block_size()];
let rest = crypter.finalize(&mut final_block)?;
final_block.truncate(rest);
if !final_block.is_empty() {
yield Bytes::from(final_block);
}
};
Ok((stream, encrypted_key))
Ok(ReceiverStream::new(rx))
}
pub async fn decrypt_file_stream_gcm(
encrypted_path: PathBuf,
decrypted_path: PathBuf,
master_key_b64: String,
) -> Result<()> {
info!("Decrypting {:?}", decrypted_path);
let master_key_bytes = general_purpose::STANDARD
.decode(master_key_b64)
.map_err(|_| anyhow::anyhow!("Invalid base64"))?;
let mut reader = BufReader::new(File::open(&encrypted_path).await?);
let mut header_line = Vec::new();
reader.read_until(b'\n', &mut header_line).await?;
let header: FileHeader = serde_json::from_slice(&header_line)?;
let key = Key::<Aes256Gcm>::try_from(master_key_bytes.as_slice())
.map_err(|_| anyhow::anyhow!("Invalid AES-256 key length"))?;
let cipher = Aes256Gcm::new(&key);
let mut writer = BufWriter::new(File::create(&decrypted_path).await?);
let mut chunk_index: u32 = 0;
loop {
let mut len_buf = [0u8; 4];
match reader.read_exact(&mut len_buf).await {
Ok(_) => {}
Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => break,
Err(e) => return Err(e.into()),
}
let chunk_len = u32::from_be_bytes(len_buf) as usize;
let mut chunk_ciphertext = vec![0u8; chunk_len];
reader.read_exact(&mut chunk_ciphertext).await?;
let mut nonce_bytes = [0u8; 12];
nonce_bytes[..8].copy_from_slice(&header.base_nonce);
nonce_bytes[8..].copy_from_slice(&chunk_index.to_be_bytes());
let nonce = Nonce::try_from(&nonce_bytes[..])
.map_err(|_| anyhow::anyhow!("Invalid nonce length"))?;
let plaintext = cipher
.decrypt(&nonce, chunk_ciphertext.as_slice())
.map_err(|e| anyhow::anyhow!("AES-GCM decryption failed: {:?}", e))?;
writer.write_all(&plaintext).await?;
chunk_index += 1;
}
writer.flush().await?;
Ok(())
}
+6 -31
View File
@@ -1,51 +1,29 @@
use crate::utils::file::encrypt_file_stream;
use crate::utils::file::encrypt_file_stream_gcm;
use anyhow::Result;
use bytes::Bytes;
use futures::{Stream, StreamExt};
use rand::RngCore;
use std::pin::Pin;
use tokio_util::io::ReaderStream;
pub struct EncryptionMetadata {
pub encrypted_aes_key: Vec<u8>,
pub iv: [u8; 16],
}
pub struct UploadStream {
pub stream: Pin<Box<dyn Stream<Item = Result<Bytes, std::io::Error>> + Send>>,
pub encryption: Option<EncryptionMetadata>,
}
pub async fn build_stream(
file_path: &std::path::Path,
encrypt: bool,
public_key_pem: Option<Vec<u8>>,
master_key_b64: &String,
) -> Result<UploadStream> {
if encrypt {
let public_key =
public_key_pem.ok_or_else(|| anyhow::anyhow!("Missing public key for encryption"))?;
let mut aes_key = [0u8; 32];
rand::rng().fill_bytes(&mut aes_key);
let mut iv = [0u8; 16];
rand::rng().fill_bytes(&mut iv);
let (encrypted_stream, encrypted_aes_key) =
encrypt_file_stream(file_path.to_path_buf(), aes_key, iv, public_key).await?;
let encrypted_stream =
encrypt_file_stream_gcm(file_path.to_path_buf(), master_key_b64.to_string()).await?;
let stream = Box::pin(
encrypted_stream
.map(|r| r.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))),
);
Ok(UploadStream {
stream,
encryption: Some(EncryptionMetadata {
encrypted_aes_key,
iv,
}),
})
Ok(UploadStream { stream })
} else {
let file = tokio::fs::File::open(file_path).await?;
let reader = ReaderStream::new(file);
@@ -54,9 +32,6 @@ pub async fn build_stream(
reader.map(|r| r.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))),
);
Ok(UploadStream {
stream,
encryption: None,
})
Ok(UploadStream { stream })
}
}