diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index c1bdc97a83..69e71392f2 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -43,13 +43,23 @@ jobs: with: node-version: 'lts/*' + - name: Cache Compute File Server CLI + id: cache-compute-file-server-cli + uses: actions/cache@v3 + with: + path: "/home/runner/.cargo/bin/compute-file-server-cli" + key: crate-cache-compute-file-server-cli + - name: Install Compute File Server CLI + if: steps.cache-compute-file-server-cli.outputs.cache-hit != 'true' + run: cd compute-file-server-cli && cargo install --path . + - run: npm update working-directory: ./documentation - run: npm run add-fastly-prefix working-directory: ./documentation - run: npm run docusaurus docs:version "$(npm pkg get version --json --prefix=../ | jq -r)" working-directory: ./documentation - + - run: npm update working-directory: ./documentation/app - run: npm run build:files diff --git a/compute-file-server-cli/.gitignore b/compute-file-server-cli/.gitignore new file mode 100644 index 0000000000..ea8c4bf7f3 --- /dev/null +++ b/compute-file-server-cli/.gitignore @@ -0,0 +1 @@ +/target diff --git a/compute-file-server-cli/Cargo.lock b/compute-file-server-cli/Cargo.lock new file mode 100644 index 0000000000..1670f76205 --- /dev/null +++ b/compute-file-server-cli/Cargo.lock @@ -0,0 +1,1437 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" + +[[package]] +name = "bytes" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" + +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "4.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69d64e88428747154bd8bc378d178377ef4dace7a5735ca1f3855be72f2c2cb5" +dependencies = [ + "atty", + "bitflags", + "clap_lex", + "strsim", + "termcolor", +] + +[[package]] +name = "clap_lex" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "combine" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "compute-file-server-cli" +version = "1.1.0" +dependencies = [ + "base64", + "clap", + "fastly-api", + "futures", + "httpdate", + "indicatif", + "openssl", + "percent-encoding", + "phf", + "reqwest", + "serde", + "serde_derive", + "serde_json", + "sha2", + "simple-error", + "tokio", + "toml_edit", + "walkdir", +] + +[[package]] +name = "console" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c050367d967ced717c04b65d8c619d863ef9292ce0c5760028655a2fb298718c" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "terminal_size", + "unicode-width", + "winapi", +] + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + +[[package]] +name = "cpufeatures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "either" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" + +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + +[[package]] +name = "encoding_rs" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "fastly-api" +version = "1.0.0-beta.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "473b140583463b51366839f3198f19193e5a2befc790298e0d6af165c47f19eb" +dependencies = [ + "reqwest", + "serde", + "serde_derive", + "serde_json", + "url", +] + +[[package]] +name = "fastrand" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +dependencies = [ + "instant", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f21eda599937fba36daeb58a22e8f5cee2d14c4a17b5b7739c7c8e5e3b8230c" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" + +[[package]] +name = "futures-executor" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ff63c23854bee61b6e9cd331d523909f238fc7636290b96826e9cfa5faa00ab" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68" + +[[package]] +name = "futures-macro" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42cd15d1c7456c04dbdf7e88bcd69760d74f3a798d6444e16974b505b0e62f17" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" + +[[package]] +name = "futures-task" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" + +[[package]] +name = "futures-util" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "h2" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca32592cf21ac7ccab1825cd87f6c9b3d9022c44d086172ed0966bec8af30be" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "http" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "hyper" +version = "0.14.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "indicatif" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfddc9561e8baf264e0e45e197fd7696320026eb10a8180340debc27b18f535b" +dependencies = [ + "console", + "number_prefix", + "unicode-width", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "ipnet" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" + +[[package]] +name = "js-sys" +version = "0.3.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.135" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c" + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "mime_guess" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "mio" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys", +] + +[[package]] +name = "native-tls" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "number_prefix" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" + +[[package]] +name = "once_cell" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" + +[[package]] +name = "openssl" +version = "0.10.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-src" +version = "111.22.0+1.1.1q" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f31f0d509d1c1ae9cada2f9539ff8f37933831fd5098879e482aa687d659853" +dependencies = [ + "cc", +] + +[[package]] +name = "openssl-sys" +version = "0.9.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5230151e44c0f05157effb743e8d517472843121cf9243e8b81393edb5acd9ce" +dependencies = [ + "autocfg", + "cc", + "libc", + "openssl-src", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "os_str_bytes" +version = "6.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + +[[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + +[[package]] +name = "phf" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928c6535de93548188ef63bb7c4036bd415cd8f36ad25af44b9789b2ee72a48c" +dependencies = [ + "phf_macros", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1181c94580fa345f50f19d738aaa39c0ed30a600d95cb2d3e23f94266f14fbf" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_macros" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92aacdc5f16768709a569e913f7451034034178b05bdc8acda226659a3dccc66" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "phf_shared" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1fb5f6f826b772a8d4c0394209441e7d37cbbb967ae9c7e0e8134365c9ee676" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" + +[[package]] +name = "proc-macro2" +version = "1.0.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "reqwest" +version = "0.11.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "431949c384f4e2ae07605ccaa56d1d9d2ecdb5cadd4f9577ccfab29f2e5149fc" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "mime_guess", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-native-tls", + "tokio-util", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + +[[package]] +name = "ryu" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" +dependencies = [ + "lazy_static", + "windows-sys", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "security-framework" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" + +[[package]] +name = "serde_derive" +version = "1.0.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41feea4228a6f1cd09ec7a3593a682276702cd67b5273544757dae23c096f074" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + +[[package]] +name = "simple-error" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc47a29ce97772ca5c927f75bac34866b16d64e07f330c3248e2d7226623901b" + +[[package]] +name = "siphasher" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" + +[[package]] +name = "slab" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "socket2" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fcd952facd492f9be3ef0d0b7032a6e442ee9b361d4acc2b1d0c4aaa5f613a1" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "terminal_size" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "1.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "winapi", +] + +[[package]] +name = "tokio-macros" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "toml_edit" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5376256e44f2443f8896ac012507c19a012df0fe8758b55246ae51a2279db51f" +dependencies = [ + "combine", + "indexmap", + "itertools", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + +[[package]] +name = "typenum" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" + +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" + +[[package]] +name = "unicode-ident" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[package]] +name = "url" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" + +[[package]] +name = "web-sys" +version = "0.3.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" + +[[package]] +name = "windows_i686_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" + +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" + +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] diff --git a/compute-file-server-cli/Cargo.toml b/compute-file-server-cli/Cargo.toml new file mode 100644 index 0000000000..027fde065f --- /dev/null +++ b/compute-file-server-cli/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "compute-file-server-cli" +version = "1.1.0" +edition = "2021" +description = "Uploads files to Fastly for serving directly from within Fastly Compute applications. Upload any type of file: images, text, video etc and serve directly from Fastly. It is ideal for serving files built from a static site generator such as 11ty." +license = "MIT" +license-file = "LICENSE" +repository = "https://github.com/jakeChampion/compute-file-server" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +clap = "4.0.10" +walkdir = "2.3.2" +reqwest = { version = "0.11", features = ["json", "stream"] } +openssl = { version = "0.10.42", features = ["vendored"] } +tokio = { version = "1", features = ["full"] } +simple-error = "0.2.3" +serde_derive = "1.0.145" +serde = "1.0.145" +phf = { version = "0.11", features = ["macros"] } +fastly-api = "1.0.0-beta.0" +indicatif = "0.17.1" +futures = "0.3.24" +percent-encoding = "2.2.0" +toml_edit = "0.14.4" +httpdate = "1.0.2" +serde_json = "1.0.86" +sha2 = "0.10.6" +base64 = "0.13.0" diff --git a/compute-file-server-cli/LICENSE b/compute-file-server-cli/LICENSE new file mode 100644 index 0000000000..f0dd09a0cf --- /dev/null +++ b/compute-file-server-cli/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Jake Daniel Champion + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/compute-file-server-cli/README.md b/compute-file-server-cli/README.md new file mode 100644 index 0000000000..ded7956201 --- /dev/null +++ b/compute-file-server-cli/README.md @@ -0,0 +1,85 @@ +# compute-file-server + +Fastly File Server uploads files to Fastly for serving directly from within Fastly Compute applications. + +Upload any type of file: images, text, video etc and serve directly from Fastly. + +It is ideal for serving files built from a static site generator such as 11ty. + +## Install + +### npm + +Install pre-compiled binaries via `npm` + +```sh +npm install compute-file-server +``` + +### Cargo + +Compile and install via `cargo` + +```sh +git clone https://github.com/JakeChampion/compute-file-server +cd compute-file-server/cli +cargo install --path . +``` + +## Commands + +### Upload + +Upload files to a Fastly Object Store, creating the Object Store if it does not exist. + +Example: `compute-file-server upload --name website-static-files -- ./folder/of/files` + +```sh +compute-file-server upload +Upload files + +Usage: compute-file-server upload [OPTIONS] --name -- + +Arguments: + + +Options: + --name + --token + -h, --help Print help information +``` + +### Link + +Connect a Fastly Object Store to a Fastly Service. + +Example: `compute-file-server link --name website-static-files --link-name files --service-id xxyyzz` + +```sh +Usage: compute-file-server link [OPTIONS] --name --link-name --service-id + +Options: + --name + --token + --link-name + --service-id + -h, --help Print help information +``` + +### Local + +Update `fastly.toml` to contain a local Object Store containing the specified files. + +Example: `compute-file-server local --name files --toml fastly.toml -- ./folder/of/files` + +```sh +Usage: compute-file-server local --toml --name -- + +Arguments: + + +Options: + --toml + --name + -h, --help Print help information +``` \ No newline at end of file diff --git a/compute-file-server-cli/rust-toolchain.toml b/compute-file-server-cli/rust-toolchain.toml new file mode 100644 index 0000000000..b9901d209f --- /dev/null +++ b/compute-file-server-cli/rust-toolchain.toml @@ -0,0 +1,4 @@ +[toolchain] +channel = "1.77.1" +targets = [ "wasm32-wasi" ] +profile = "minimal" diff --git a/compute-file-server-cli/src/main.rs b/compute-file-server-cli/src/main.rs new file mode 100644 index 0000000000..d43ca6aa2d --- /dev/null +++ b/compute-file-server-cli/src/main.rs @@ -0,0 +1,958 @@ +use base64; +use clap::{arg, Command}; +use fastly_api::apis::configuration::{ApiKey, Configuration}; +use fastly_api::apis::version_api::{ + activate_service_version, clone_service_version, list_service_versions, + ActivateServiceVersionParams, CloneServiceVersionParams, ListServiceVersionsParams, +}; +use futures::{stream, StreamExt}; +use httpdate::fmt_http_date; +use reqwest::Client; +use sha2::{Digest, Sha256}; +use simple_error::bail; +use std::error::Error; +use std::path::PathBuf; +use tokio; +use tokio::fs::File; +use toml_edit; +use walkdir::WalkDir; + +const PARALLEL_REQUESTS: usize = 10; +const RETRY_REQUESTS: usize = 5; + +use phf::phf_map; + +static MIMES: phf::Map<&'static str, &'static str> = phf_map! { + "ez"=> "application/andrew-inset", + "aw"=> "application/applixware", + "atom"=> "application/atom+xml", + "atomcat"=> "application/atomcat+xml", + "atomdeleted"=> "application/atomdeleted+xml", + "atomsvc"=> "application/atomsvc+xml", + "dwd"=> "application/atsc-dwd+xml", + "held"=> "application/atsc-held+xml", + "rsat"=> "application/atsc-rsat+xml", + "bdoc"=> "application/bdoc", + "xcs"=> "application/calendar+xml", + "ccxml"=> "application/ccxml+xml", + "cdfx"=> "application/cdfx+xml", + "cdmia"=> "application/cdmi-capability", + "cdmic"=> "application/cdmi-container", + "cdmid"=> "application/cdmi-domain", + "cdmio"=> "application/cdmi-object", + "cdmiq"=> "application/cdmi-queue", + "cu"=> "application/cu-seeme", + "mpd"=> "application/dash+xml", + "davmount"=> "application/davmount+xml", + "dbk"=> "application/docbook+xml", + "dssc"=> "application/dssc+der", + "xdssc"=> "application/dssc+xml", + "es"=> "application/ecmascript", + "ecma"=> "application/ecmascript", + "emma"=> "application/emma+xml", + "emotionml"=> "application/emotionml+xml", + "epub"=> "application/epub+zip", + "exi"=> "application/exi", + "fdt"=> "application/fdt+xml", + "pfr"=> "application/font-tdpfr", + "geojson"=> "application/geo+json", + "gml"=> "application/gml+xml", + "gpx"=> "application/gpx+xml", + "gxf"=> "application/gxf", + "gz"=> "application/gzip", + "hjson"=> "application/hjson", + "stk"=> "application/hyperstudio", + "ink"=> "application/inkml+xml", + "inkml"=> "application/inkml+xml", + "ipfix"=> "application/ipfix", + "its"=> "application/its+xml", + "jar"=> "application/java-archive", + "war"=> "application/java-archive", + "ear"=> "application/java-archive", + "ser"=> "application/java-serialized-object", + "class"=> "application/java-vm", + "js"=> "application/javascript", + "mjs"=> "application/javascript", + "json"=> "application/json", + "map"=> "application/json", + "json5"=> "application/json5", + "jsonml"=> "application/jsonml+json", + "jsonld"=> "application/ld+json", + "lgr"=> "application/lgr+xml", + "lostxml"=> "application/lost+xml", + "hqx"=> "application/mac-binhex40", + "cpt"=> "application/mac-compactpro", + "mads"=> "application/mads+xml", + "webmanifest"=> "application/manifest+json", + "mrc"=> "application/marc", + "mrcx"=> "application/marcxml+xml", + "ma"=> "application/mathematica", + "nb"=> "application/mathematica", + "mb"=> "application/mathematica", + "mathml"=> "application/mathml+xml", + "mbox"=> "application/mbox", + "mscml"=> "application/mediaservercontrol+xml", + "metalink"=> "application/metalink+xml", + "meta4"=> "application/metalink4+xml", + "mets"=> "application/mets+xml", + "maei"=> "application/mmt-aei+xml", + "musd"=> "application/mmt-usd+xml", + "mods"=> "application/mods+xml", + "m21"=> "application/mp21", + "mp21"=> "application/mp21", + "mp4s"=> "application/mp4", + "m4p"=> "application/mp4", + "doc"=> "application/msword", + "dot"=> "application/msword", + "mxf"=> "application/mxf", + "nq"=> "application/n-quads", + "nt"=> "application/n-triples", + "cjs"=> "application/node", + "bin"=> "application/octet-stream", + "dms"=> "application/octet-stream", + "lrf"=> "application/octet-stream", + "mar"=> "application/octet-stream", + "so"=> "application/octet-stream", + "dist"=> "application/octet-stream", + "distz"=> "application/octet-stream", + "pkg"=> "application/octet-stream", + "bpk"=> "application/octet-stream", + "dump"=> "application/octet-stream", + "elc"=> "application/octet-stream", + "deploy"=> "application/octet-stream", + "exe"=> "application/octet-stream", + "dll"=> "application/octet-stream", + "deb"=> "application/octet-stream", + "dmg"=> "application/octet-stream", + "iso"=> "application/octet-stream", + "img"=> "application/octet-stream", + "msi"=> "application/octet-stream", + "msp"=> "application/octet-stream", + "msm"=> "application/octet-stream", + "buffer"=> "application/octet-stream", + "oda"=> "application/oda", + "opf"=> "application/oebps-package+xml", + "ogx"=> "application/ogg", + "omdoc"=> "application/omdoc+xml", + "onetoc"=> "application/onenote", + "onetoc2"=> "application/onenote", + "onetmp"=> "application/onenote", + "onepkg"=> "application/onenote", + "oxps"=> "application/oxps", + "relo"=> "application/p2p-overlay+xml", + "xer"=> "application/patch-ops-error+xml", + "pdf"=> "application/pdf", + "pgp"=> "application/pgp-encrypted", + "asc"=> "application/pgp-signature", + "sig"=> "application/pgp-signature", + "prf"=> "application/pics-rules", + "p10"=> "application/pkcs10", + "p7m"=> "application/pkcs7-mime", + "p7c"=> "application/pkcs7-mime", + "p7s"=> "application/pkcs7-signature", + "p8"=> "application/pkcs8", + "ac"=> "application/pkix-attr-cert", + "cer"=> "application/pkix-cert", + "crl"=> "application/pkix-crl", + "pkipath"=> "application/pkix-pkipath", + "pki"=> "application/pkixcmp", + "pls"=> "application/pls+xml", + "ai"=> "application/postscript", + "eps"=> "application/postscript", + "ps"=> "application/postscript", + "provx"=> "application/provenance+xml", + "cww"=> "application/prs.cww", + "pskcxml"=> "application/pskc+xml", + "raml"=> "application/raml+yaml", + "rdf"=> "application/rdf+xml", + "owl"=> "application/rdf+xml", + "rif"=> "application/reginfo+xml", + "rnc"=> "application/relax-ng-compact-syntax", + "rl"=> "application/resource-lists+xml", + "rld"=> "application/resource-lists-diff+xml", + "rs"=> "application/rls-services+xml", + "rapd"=> "application/route-apd+xml", + "sls"=> "application/route-s-tsid+xml", + "rusd"=> "application/route-usd+xml", + "gbr"=> "application/rpki-ghostbusters", + "mft"=> "application/rpki-manifest", + "roa"=> "application/rpki-roa", + "rsd"=> "application/rsd+xml", + "rss"=> "application/rss+xml", + "rtf"=> "application/rtf", + "sbml"=> "application/sbml+xml", + "scq"=> "application/scvp-cv-request", + "scs"=> "application/scvp-cv-response", + "spq"=> "application/scvp-vp-request", + "spp"=> "application/scvp-vp-response", + "sdp"=> "application/sdp", + "senmlx"=> "application/senml+xml", + "sensmlx"=> "application/sensml+xml", + "setpay"=> "application/set-payment-initiation", + "setreg"=> "application/set-registration-initiation", + "shf"=> "application/shf+xml", + "siv"=> "application/sieve", + "sieve"=> "application/sieve", + "smi"=> "application/smil+xml", + "smil"=> "application/smil+xml", + "rq"=> "application/sparql-query", + "srx"=> "application/sparql-results+xml", + "gram"=> "application/srgs", + "grxml"=> "application/srgs+xml", + "sru"=> "application/sru+xml", + "ssdl"=> "application/ssdl+xml", + "ssml"=> "application/ssml+xml", + "swidtag"=> "application/swid+xml", + "tei"=> "application/tei+xml", + "teicorpus"=> "application/tei+xml", + "tfi"=> "application/thraud+xml", + "tsd"=> "application/timestamped-data", + "toml"=> "application/toml", + "trig"=> "application/trig", + "ttml"=> "application/ttml+xml", + "ubj"=> "application/ubjson", + "rsheet"=> "application/urc-ressheet+xml", + "td"=> "application/urc-targetdesc+xml", + "vxml"=> "application/voicexml+xml", + "wasm"=> "application/wasm", + "wgt"=> "application/widget", + "hlp"=> "application/winhlp", + "wsdl"=> "application/wsdl+xml", + "wspolicy"=> "application/wspolicy+xml", + "xaml"=> "application/xaml+xml", + "xav"=> "application/xcap-att+xml", + "xca"=> "application/xcap-caps+xml", + "xdf"=> "application/xcap-diff+xml", + "xel"=> "application/xcap-el+xml", + "xns"=> "application/xcap-ns+xml", + "xenc"=> "application/xenc+xml", + "xhtml"=> "application/xhtml+xml", + "xht"=> "application/xhtml+xml", + "xlf"=> "application/xliff+xml", + "xml"=> "application/xml", + "xsl"=> "application/xml", + "xsd"=> "application/xml", + "rng"=> "application/xml", + "dtd"=> "application/xml-dtd", + "xop"=> "application/xop+xml", + "xpl"=> "application/xproc+xml", + "xslt"=> "application/xml", + "xspf"=> "application/xspf+xml", + "mxml"=> "application/xv+xml", + "xhvml"=> "application/xv+xml", + "xvml"=> "application/xv+xml", + "xvm"=> "application/xv+xml", + "yang"=> "application/yang", + "yin"=> "application/yin+xml", + "zip"=> "application/zip", + "3gpp"=> "video/3gpp", + "adp"=> "audio/adpcm", + "amr"=> "audio/amr", + "au"=> "audio/basic", + "snd"=> "audio/basic", + "mid"=> "audio/midi", + "midi"=> "audio/midi", + "kar"=> "audio/midi", + "rmi"=> "audio/midi", + "mxmf"=> "audio/mobile-xmf", + "mp3"=> "audio/mpeg", + "m4a"=> "audio/mp4", + "mp4a"=> "audio/mp4", + "mpga"=> "audio/mpeg", + "mp2"=> "audio/mpeg", + "mp2a"=> "audio/mpeg", + "m2a"=> "audio/mpeg", + "m3a"=> "audio/mpeg", + "oga"=> "audio/ogg", + "ogg"=> "audio/ogg", + "spx"=> "audio/ogg", + "opus"=> "audio/ogg", + "s3m"=> "audio/s3m", + "sil"=> "audio/silk", + "wav"=> "audio/wav", + "weba"=> "audio/webm", + "xm"=> "audio/xm", + "ttc"=> "font/collection", + "otf"=> "font/otf", + "ttf"=> "font/ttf", + "woff"=> "font/woff", + "woff2"=> "font/woff2", + "exr"=> "image/aces", + "apng"=> "image/apng", + "avif"=> "image/avif", + "bmp"=> "image/bmp", + "cgm"=> "image/cgm", + "drle"=> "image/dicom-rle", + "emf"=> "image/emf", + "fits"=> "image/fits", + "g3"=> "image/g3fax", + "gif"=> "image/gif", + "heic"=> "image/heic", + "heics"=> "image/heic-sequence", + "heif"=> "image/heif", + "heifs"=> "image/heif-sequence", + "hej2"=> "image/hej2k", + "hsj2"=> "image/hsj2", + "ief"=> "image/ief", + "jls"=> "image/jls", + "jp2"=> "image/jp2", + "jpg2"=> "image/jp2", + "jpeg"=> "image/jpeg", + "jpg"=> "image/jpeg", + "jpe"=> "image/jpeg", + "jph"=> "image/jph", + "jhc"=> "image/jphc", + "jpm"=> "image/jpm", + "jpx"=> "image/jpx", + "jpf"=> "image/jpx", + "jxr"=> "image/jxr", + "jxra"=> "image/jxra", + "jxrs"=> "image/jxrs", + "jxs"=> "image/jxs", + "jxsc"=> "image/jxsc", + "jxsi"=> "image/jxsi", + "jxss"=> "image/jxss", + "ktx"=> "image/ktx", + "ktx2"=> "image/ktx2", + "png"=> "image/png", + "btif"=> "image/prs.btif", + "pti"=> "image/prs.pti", + "sgi"=> "image/sgi", + "svg"=> "image/svg+xml", + "svgz"=> "image/svg+xml", + "t38"=> "image/t38", + "tif"=> "image/tiff", + "tiff"=> "image/tiff", + "tfx"=> "image/tiff-fx", + "webp"=> "image/webp", + "wmf"=> "image/wmf", + "disposition-notification"=> "message/disposition-notification", + "u8msg"=> "message/global", + "u8dsn"=> "message/global-delivery-status", + "u8mdn"=> "message/global-disposition-notification", + "u8hdr"=> "message/global-headers", + "eml"=> "message/rfc822", + "mime"=> "message/rfc822", + "3mf"=> "model/3mf", + "gltf"=> "model/gltf+json", + "glb"=> "model/gltf-binary", + "igs"=> "model/iges", + "iges"=> "model/iges", + "msh"=> "model/mesh", + "mesh"=> "model/mesh", + "silo"=> "model/mesh", + "mtl"=> "model/mtl", + "obj"=> "model/obj", + "stpz"=> "model/step+zip", + "stpxz"=> "model/step-xml+zip", + "stl"=> "model/stl", + "wrl"=> "model/vrml", + "vrml"=> "model/vrml", + "x3db"=> "model/x3d+fastinfoset", + "x3dbz"=> "model/x3d+binary", + "x3dv"=> "model/x3d-vrml", + "x3dvz"=> "model/x3d+vrml", + "x3d"=> "model/x3d+xml", + "x3dz"=> "model/x3d+xml", + "appcache"=> "text/cache-manifest", + "manifest"=> "text/cache-manifest", + "ics"=> "text/calendar", + "ifb"=> "text/calendar", + "coffee"=> "text/coffeescript", + "litcoffee"=> "text/coffeescript", + "css"=> "text/css", + "csv"=> "text/csv", + "html"=> "text/html", + "htm"=> "text/html", + "shtml"=> "text/html", + "jade"=> "text/jade", + "jsx"=> "text/jsx", + "less"=> "text/less", + "markdown"=> "text/markdown", + "md"=> "text/markdown", + "mml"=> "text/mathml", + "mdx"=> "text/mdx", + "n3"=> "text/n3", + "txt"=> "text/plain", + "text"=> "text/plain", + "conf"=> "text/plain", + "def"=> "text/plain", + "list"=> "text/plain", + "log"=> "text/plain", + "in"=> "text/plain", + "ini"=> "text/plain", + "dsc"=> "text/prs.lines.tag", + "rtx"=> "text/richtext", + "sgml"=> "text/sgml", + "sgm"=> "text/sgml", + "shex"=> "text/shex", + "slim"=> "text/slim", + "slm"=> "text/slim", + "spdx"=> "text/spdx", + "stylus"=> "text/stylus", + "styl"=> "text/stylus", + "tsv"=> "text/tab-separated-values", + "t"=> "text/troff", + "tr"=> "text/troff", + "roff"=> "text/troff", + "man"=> "text/troff", + "me"=> "text/troff", + "ms"=> "text/troff", + "ttl"=> "text/turtle", + "uri"=> "text/uri-list", + "uris"=> "text/uri-list", + "urls"=> "text/uri-list", + "vcard"=> "text/vcard", + "vtt"=> "text/vtt", + "yaml"=> "text/yaml", + "yml"=> "text/yaml", + "3gp"=> "video/3gpp", + "3g2"=> "video/3gpp2", + "h261"=> "video/h261", + "h263"=> "video/h263", + "h264"=> "video/h264", + "m4s"=> "video/iso.segment", + "jpgv"=> "video/jpeg", + "jpgm"=> "image/jpm", + "mj2"=> "video/mj2", + "mjp2"=> "video/mj2", + "ts"=> "video/mp2t", + "mp4"=> "video/mp4", + "mp4v"=> "video/mp4", + "mpg4"=> "video/mp4", + "mpeg"=> "video/mpeg", + "mpg"=> "video/mpeg", + "mpe"=> "video/mpeg", + "m1v"=> "video/mpeg", + "m2v"=> "video/mpeg", + "ogv"=> "video/ogg", + "qt"=> "video/quicktime", + "mov"=> "video/quicktime", + "webm"=> "video/webm" +}; + +fn lookup(extn: &str) -> Option<&&str> { + let extn = extn.trim().to_lowercase(); + MIMES.get(&extn) +} + +use serde_derive::Deserialize; +use serde_derive::Serialize; + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +struct Metadata { + #[serde(rename = "ETag")] + etag: String, + #[serde(rename = "Last-Modified")] + last_modified: String, + #[serde(rename = "Content-Type")] + content_type: Option, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +struct KVStores { + data: Vec, + meta: Meta, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +struct KVStore { + id: String, + name: String, + #[serde(rename = "created_at")] + created_at: String, + #[serde(rename = "updated_at")] + updated_at: String, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +struct Meta { + limit: i64, + total: i64, +} + +async fn create_store(name: &str, token: &str) -> Result> { + let client = reqwest::Client::new(); + let res = client + .post("https://api.fastly.com/resources/stores/kv") + .header("Content-Type", "application/json") + .header("Accept", "application/json") + .header("Fastly-Key", token) + .body(format!("{{\"name\":\"{}\"}}", name)) + .send() + .await?; + if res.status() == 201 { + Ok(res.json::().await?.id) + } else { + bail!(format!( + "Failed to create Object Store named `{}`. Response body contained `{}`", + name, + res.text().await? + )) + } +} + +async fn get_or_create_store( + name: &str, + token: &str, +) -> Result> { + // get all stores + let client = reqwest::Client::new(); + let res = client + .get("https://api.fastly.com/resources/stores/kv") + .header("Content-Type", "application/json") + .header("Accept", "application/json") + .header("Fastly-Key", token) + .send() + .await?; + // if no stores at all, create store + if res.status() == 404 { + create_store(name, token).await + } else { + // check if store already exists + let res = res + .json::() + .await? + .data + .into_iter() + .find_map(|store| { + if store.name == name { + Some(store.id) + } else { + None + } + }); + // if store does not exist, create store + if res.is_none() { + create_store(name, token).await + } else { + Ok(res.unwrap()) + } + } +} + +async fn get_active_version_of_service( + service_id: &str, + token: &str, +) -> Result> { + let mut cfg = Configuration { + api_key: Some(ApiKey { + prefix: None, + key: token.to_owned(), + }), + ..Default::default() + }; + + let params = ListServiceVersionsParams { + service_id: service_id.to_owned(), + ..Default::default() + }; + + let result = list_service_versions(&mut cfg, params) + .await? + .into_iter() + .find_map(|v| { + if v.active.unwrap() { + Some(v.number.unwrap()) + } else { + None + } + }); + + return Ok(result.expect("Service should have an active version to clone")); +} + +async fn clone_version_of_service( + service_id: &str, + token: &str, + version: i32, +) -> Result> { + let mut cfg = Configuration { + api_key: Some(ApiKey { + prefix: None, + key: token.to_owned(), + }), + ..Default::default() + }; + + let params = CloneServiceVersionParams { + service_id: service_id.to_owned(), + version_id: version, + ..Default::default() + }; + + Ok(clone_service_version(&mut cfg, params).await?.number.unwrap()) +} + +async fn activate_version_of_service( + service_id: &str, + token: &str, + version: i32, +) -> Result> { + let mut cfg = Configuration { + api_key: Some(ApiKey { + prefix: None, + key: token.to_owned(), + }), + ..Default::default() + }; + + let params = ActivateServiceVersionParams { + service_id: service_id.to_string(), + version_id: version, + ..Default::default() + }; + + Ok(activate_service_version(&mut cfg, params).await?.number.unwrap()) +} + +fn cli() -> Command { + Command::new("fastly-file-server") + .about("Fastly File Server uploads files to Fastly for serving directly from within Fastly Compute applications. Upload any type of file: images, text, video etc and serve directly from Fastly. It is ideal for serving files built from a static site generator such as 11ty.") + .subcommand_required(true) + .arg_required_else_help(true) + .subcommand( + Command::new("upload") + .about("Upload files") + .arg( + arg!(path: [PATH]) + .last(true) + .required(true) + .value_parser(clap::value_parser!(PathBuf)), + ) + .arg_required_else_help(true) + .arg(arg!(--name ).required(true)) + .arg(arg!(--token )), + ) + .subcommand( + Command::new("local") + .about("Setup files") + .arg( + arg!(path: [PATH]) + .last(true) + .required(true) + .value_parser(clap::value_parser!(PathBuf)), + ) + .arg_required_else_help(true) + .arg(arg!(--toml ).required(true).value_parser(clap::value_parser!(PathBuf))) + .arg(arg!(--name ).required(true)), + ) + .subcommand( + Command::new("link") + .about("link store to service") + .arg(arg!(--name ).required(true)) + .arg(arg!(--token )) + .arg(arg!(--"link-name" ).required(true)) + .arg(arg!(--"service-id" ).required(true)), + ) + .subcommand( + Command::new("unlink") + .about("unlink store to service") + .arg(arg!(--name ).required(true)) + .arg(arg!(--token )) + .arg(arg!(--"link-name" ).required(true)) + .arg(arg!(--"service-id" ).required(true)), + ) +} + +async fn link(sub_matches: &clap::ArgMatches) -> Result<(), Box> { + let service_id = sub_matches + .get_one::("service-id") + .map(|s| s.as_str()) + .expect("required in clap"); + + let link_name = sub_matches + .get_one::("link-name") + .map(|s| s.as_str()) + .expect("required in clap"); + + let name = sub_matches + .get_one::("name") + .map(|s| s.as_str()) + .expect("required in clap"); + + let token = sub_matches + .get_one::("token") + .map(|s| s.to_owned()) + .or_else(|| match std::env::var("FASTLY_API_TOKEN") { + Ok(x) => Some(x), + Err(_) => None, + }); + if token.is_none() { + bail!("Missing Fastly API token. Please provide an API token via the --token argument or the FASTLY_API_TOKEN environment variable.") + } + let token = token.unwrap(); + + let store_id = get_or_create_store(name, &token).await?; + + let version = get_active_version_of_service(service_id, &token).await?; + let version = clone_version_of_service(service_id, &token, version).await?; + + // link + let client = reqwest::Client::new(); + let _res = client + .post(format!( + "https://api.fastly.com/service/{}/version/{}/resource", + service_id, version + )) + .header("Content-Type", "application/x-www-form-urlencoded") + .header("Accept", "application/json") + .header("Fastly-Key", &token) + .body(format!("name={}&resource_id={}", link_name, store_id)) + .send() + .await?; + + // activate + activate_version_of_service(service_id, &token, version).await?; + + Ok(()) +} + +async fn upload(sub_matches: &clap::ArgMatches) -> Result<(), Box> { + let name = sub_matches + .get_one::("name") + .map(|s| s.as_str()) + .expect("required in clap"); + + let token = sub_matches + .get_one::("token") + .map(|s| s.to_owned()) + .or_else(|| match std::env::var("FASTLY_API_TOKEN") { + Ok(x) => Some(x), + Err(_) => None, + }); + if token.is_none() { + bail!("Missing Fastly API token. Please provide an API token via the --token argument or the FASTLY_API_TOKEN environment variable.") + } + let token = token.unwrap(); + let store_id = get_or_create_store(name, &token).await?; + + let path = sub_matches + .get_one::("path") + .expect("required in clap"); + + let entries = WalkDir::new(path).follow_links(true) + .into_iter() + .filter_map(Result::ok) + .filter(|e| !e.file_type().is_dir()) + .collect::>(); + + let pb = indicatif::ProgressBar::new(entries.len().try_into().unwrap()); + let client = Client::new(); + + let bodies = stream::iter(entries) + .map(|entry| -> tokio::task::JoinHandle>> { + let path = path.clone(); + let store_id = store_id.clone(); + let token = token.clone(); + let client = client.clone(); + tokio::spawn(async move { + let extension = entry.path().extension().map(|e| e.to_string_lossy().to_string()).unwrap_or("".to_string()); + let normalised_entry = entry.path().strip_prefix(path).unwrap(); + let normalised_path = "/".to_owned() + &normalised_entry.to_string_lossy(); + let key = percent_encoding::utf8_percent_encode( + &normalised_path, + percent_encoding::NON_ALPHANUMERIC, + ); + let metadata_key = normalised_path.to_owned() + "__metadata__"; + let metadata_key = percent_encoding::utf8_percent_encode( + &metadata_key, + percent_encoding::NON_ALPHANUMERIC, + ); + let file_contents = tokio::fs::read(entry.path()).await?; + let file = File::open(entry.path()).await?; + let file_metadata = file.metadata().await?; + let length = file.metadata().await?.len(); + let mut counter = 0; + let sha = Sha256::digest(file_contents); + let sha = base64::encode(&sha); + let metadata = serde_json::to_string(&Metadata { + etag: format!("W/\"{}\"", sha), + last_modified: fmt_http_date(file_metadata.modified()?), + content_type: lookup(&extension).map(|content_type| content_type.to_string()) + })?; + + loop { + let res = client + .put(format!( + "https://api.fastly.com/resources/stores/kv/{}/keys/{}", + store_id, metadata_key + )) + .header("Content-Type", "application/json") + .header("Content-Length", metadata.len().to_string()) + .header("Accept", "application/json") + .header("Fastly-Key", &token) + .body(metadata.clone()) + .send() + .await?; + if res.status() != 200 { + counter = counter + 1; + if counter > RETRY_REQUESTS { + bail!( + "Error uploading metadata for file named `{}`: Response Status: {} Response Body: {}", + normalised_path, + res.status(), + res.text().await? + ); + } + } else { + break; + } + } + let mut counter = 0; + loop { + let res = client + .put(format!( + "https://api.fastly.com/resources/stores/kv/{}/keys/{}", + store_id, key + )) + .header("Content-Type", "application/json") + .header("Content-Length", length) + .header("Accept", "application/json") + .header("Fastly-Key", &token) + .body(file.try_clone().await?) + .send() + .await?; + if res.status() != 200 { + counter = counter + 1; + if counter > RETRY_REQUESTS { + bail!( + "Error uploading file named `{}`: Response Status: {} Response Body: {}", + normalised_path, + res.status(), + res.text().await? + ); + } + } else { + return Ok::>( + normalised_path, + ); + } + } + }) + }) + .buffer_unordered(PARALLEL_REQUESTS); + + bodies + .for_each(|b| async { + match b { + Ok(Ok(normalised_entry)) => { + pb.println(format!("[+] uploaded {}", normalised_entry)); + pb.inc(1); + } + Ok(Err(e)) => eprintln!("Got a reqwest::Error: {}", e), + Err(e) => eprintln!("Got a tokio::JoinError: {}", e), + } + }) + .await; + + pb.finish_with_message("done"); + Ok(()) +} + +async fn local(sub_matches: &clap::ArgMatches) -> Result<(), Box> { + let name = sub_matches + .get_one::("name") + .map(|s| s.as_str()) + .expect("required in clap"); + + let path = sub_matches + .get_one::("path") + .expect("required in clap"); + + let toml_path = sub_matches + .get_one::("toml") + .expect("required in clap"); + + let entries = WalkDir::new(path).follow_links(true) + .into_iter() + .filter_map(Result::ok) + .filter(|e| !e.file_type().is_dir()) + .collect::>(); + + let mut toml = std::fs::read_to_string(toml_path)?.parse::()?; + let mut local_server = toml + .get_key_value("local_server") + .map(|a| a.1.to_owned()) + .unwrap_or_else(|| toml_edit::table()); + let mut object_store = local_server + .as_table_mut() + .unwrap() + .get_key_value(&format!("object_store")) + .map(|a| a.1.to_owned()) + .unwrap_or_else(|| toml_edit::table()); + + let mut site = toml_edit::array(); + for entry in entries { + let path = path.clone(); + let entry_path = entry.path().to_string_lossy().to_string(); + let extension = entry + .path() + .extension() + .map(|e| e.to_string_lossy().to_string()) + .unwrap_or("".to_string()); + let normalised_entry = entry.path().strip_prefix(path).unwrap(); + let normalised_path = "/".to_owned() + &normalised_entry.to_string_lossy(); + let key = &normalised_path; + let metadata_key = normalised_path.to_owned() + "__metadata__"; + let file_contents = tokio::fs::read(entry.path()).await?; + let file = File::open(entry.path()).await?; + let file_metadata = file.metadata().await?; + let sha = Sha256::digest(file_contents); + let sha = base64::encode(&sha); + let metadata = serde_json::to_string(&Metadata { + etag: format!("W/\"{}\"", sha), + last_modified: fmt_http_date(file_metadata.modified()?), + content_type: lookup(&extension).map(|content_type| content_type.to_string()), + })?; + let mut entry = toml_edit::table(); + entry + .as_table_mut() + .unwrap() + .insert("key", toml_edit::value(metadata_key)); + entry + .as_table_mut() + .unwrap() + .insert("data", toml_edit::value(metadata.clone())); + site.as_array_of_tables_mut() + .unwrap() + .push(entry.as_table().unwrap().to_owned()); + let mut entry = toml_edit::table(); + entry + .as_table_mut() + .unwrap() + .insert("key", toml_edit::value(key)); + entry + .as_table_mut() + .unwrap() + .insert("path", toml_edit::value(entry_path)); + site.as_array_of_tables_mut() + .unwrap() + .push(entry.as_table().unwrap().to_owned()); + } + object_store.as_table_mut().unwrap().insert(name, site); + local_server + .as_table_mut() + .unwrap() + .insert(&"object_store", object_store); + toml.as_table_mut().insert("local_server", local_server); + std::fs::write(toml_path, toml.to_string())?; + + Ok(()) +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + let matches = cli().get_matches(); + + match matches.subcommand() { + Some(("link", sub_matches)) => link(sub_matches).await, + Some(("local", sub_matches)) => local(sub_matches).await, + Some(("upload", sub_matches)) => upload(sub_matches).await, + _ => unreachable!(), + } +} diff --git a/documentation/app/package-lock.json b/documentation/app/package-lock.json index 815419fda5..6b5bda2279 100644 --- a/documentation/app/package-lock.json +++ b/documentation/app/package-lock.json @@ -5,12 +5,8 @@ "packages": { "": { "license": "MIT", - "dependencies": { - "@jakechampion/c-at-e-file-server": "^0.0.2-main" - }, "devDependencies": { - "@fastly/js-compute": "^3", - "@jakechampion/c-at-e-file-server-cli": "^0.0.2-main" + "@fastly/js-compute": "^3" } }, "node_modules/@bytecodealliance/componentize-js": { @@ -660,102 +656,6 @@ "js-compute-runtime": "js-compute-runtime-cli.js" } }, - "node_modules/@jakechampion/c-at-e-file-server": { - "version": "0.0.2-main", - "resolved": "https://registry.npmjs.org/@jakechampion/c-at-e-file-server/-/c-at-e-file-server-0.0.2-main.tgz", - "integrity": "sha512-QIeZcdFPtvnPfdQL8xJD8tMezEmqihlHe5SAFJjiiDh0p1D3Eui51MsW51DFBN5r8Kv1+96MtE8ryLv1IlQwgw==", - "license": "MIT", - "dependencies": { - "range-parser": "^1.2.1" - } - }, - "node_modules/@jakechampion/c-at-e-file-server-cli": { - "version": "0.0.2-main", - "resolved": "https://registry.npmjs.org/@jakechampion/c-at-e-file-server-cli/-/c-at-e-file-server-cli-0.0.2-main.tgz", - "integrity": "sha512-j2EzKug2rIuX7AprOq041MBsuO9fIBraWP3nsLNiC1kCw3jnR/fk2+CIH15EHtQ0uuYmdKGe/Q9BM30gW0PIjQ==", - "dev": true, - "license": "MIT", - "bin": { - "c-at-e-file-server": "c-at-e-file-server.js" - }, - "engines": { - "node": ">=16" - }, - "optionalDependencies": { - "@jakechampion/c-at-e-file-server-darwin-arm64": "0.0.2-main", - "@jakechampion/c-at-e-file-server-darwin-x64": "0.0.2-main", - "@jakechampion/c-at-e-file-server-linux-x64": "0.0.2-main", - "@jakechampion/c-at-e-file-server-win32-x64": "0.0.2-main" - } - }, - "node_modules/@jakechampion/c-at-e-file-server-darwin-arm64": { - "version": "0.0.2-main", - "resolved": "https://registry.npmjs.org/@jakechampion/c-at-e-file-server-darwin-arm64/-/c-at-e-file-server-darwin-arm64-0.0.2-main.tgz", - "integrity": "sha512-TZUMtAQmzVDVFcRvxP8FixjAmMDCrzmbXltkDlvG8+hYNVVLVG/2VDmkcAmEy56LUrYiov79P/VUKMPPDVhG9g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "bin": { - "c-at-e-file-server-darwin-arm64": "c-at-e-file-server" - } - }, - "node_modules/@jakechampion/c-at-e-file-server-darwin-x64": { - "version": "0.0.2-main", - "resolved": "https://registry.npmjs.org/@jakechampion/c-at-e-file-server-darwin-x64/-/c-at-e-file-server-darwin-x64-0.0.2-main.tgz", - "integrity": "sha512-04I/dib1f1Fl9wWF29YY1PMUuRDEDv43qn6mxxAOF3QzvhtVakxkPD9ml7gF3ETr+J0QBVgZRVv2H6mOn+MIKw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "bin": { - "c-at-e-file-server-darwin-x64": "c-at-e-file-server" - } - }, - "node_modules/@jakechampion/c-at-e-file-server-linux-x64": { - "version": "0.0.2-main", - "resolved": "https://registry.npmjs.org/@jakechampion/c-at-e-file-server-linux-x64/-/c-at-e-file-server-linux-x64-0.0.2-main.tgz", - "integrity": "sha512-xL4cXyJ19WegU+w89oOlOw69FLoAqmiWJtfcsKUeUHeJjjqAmKQ98T5dsroBXQeYNXQYbt48IbX3Qa7NdMyZzw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "bin": { - "c-at-e-file-server-linux-x64": "c-at-e-file-server" - } - }, - "node_modules/@jakechampion/c-at-e-file-server-win32-x64": { - "version": "0.0.2-main", - "resolved": "https://registry.npmjs.org/@jakechampion/c-at-e-file-server-win32-x64/-/c-at-e-file-server-win32-x64-0.0.2-main.tgz", - "integrity": "sha512-wqgxzcj0EtaSDBA7ivVYLvVreLXJfFD3KkyQ0UFutGPCITr18nPsnsl7r8h6M+gc4gmL22r5VMW72Xd+wXUaEg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "bin": { - "c-at-e-file-server-win32-x64": "c-at-e-file-server" - } - }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", @@ -1915,15 +1815,6 @@ "dev": true, "license": "MIT" }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/readable-stream": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", diff --git a/documentation/app/package.json b/documentation/app/package.json index 185e0512e6..c7e2b0d633 100644 --- a/documentation/app/package.json +++ b/documentation/app/package.json @@ -1,20 +1,16 @@ { "license": "MIT", "devDependencies": { - "@fastly/js-compute": "^3", - "@jakechampion/c-at-e-file-server-cli": "^0.0.2-main" + "@fastly/js-compute": "^3" }, "type": "module", "scripts": { "build": "npm run build:app && npm run build:files", "build:app": "js-compute-runtime src/index.js", - "build:files": "c-at-e-file-server local --toml fastly.toml --name site -- ../build/", + "build:files": "compute-file-server-cli local --toml fastly.toml --name site -- ../build/", "deploy": "npm run deploy:app && npm run deploy:files", "deploy:app": "fastly compute publish", - "deploy:files": "c-at-e-file-server upload --name 'js-docs-site' -- ../build/", + "deploy:files": "compute-file-server-cli upload --name 'js-docs-site' -- ../build/", "start": "fastly compute serve" - }, - "dependencies": { - "@jakechampion/c-at-e-file-server": "^0.0.2-main" } }