♻️ 使用 Actix RS 重构 #8

Merged
LittleSheep merged 7 commits from refactor/actix-rs into refactor/rust 2024-02-13 12:39:08 +00:00
65 changed files with 7039 additions and 470 deletions
Showing only changes of commit b7d4a54d62 - Show all commits

BIN
.DS_Store vendored Normal file

Binary file not shown.

View File

@ -5,24 +5,24 @@ on:
branches: [ master ]
jobs:
build-docker:
runs-on: edge
build-image:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_REGISTRY_USERNAME }}
password: ${{ secrets.DOCKER_REGISTRY_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v4
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
push: true
tags: xsheep2010/roadsign:nightly
file: ./Dockerfile
tags: xsheep2010/roadsign:sigma

490
Cargo.lock generated
View File

@ -2,6 +2,240 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "actix-codec"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a"
dependencies = [
"bitflags 2.4.1",
"bytes",
"futures-core",
"futures-sink",
"memchr",
"pin-project-lite",
"tokio",
"tokio-util",
"tracing",
]
[[package]]
name = "actix-files"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf0bdd6ff79de7c9a021f5d9ea79ce23e108d8bfc9b49b5b4a2cf6fad5a35212"
dependencies = [
"actix-http",
"actix-service",
"actix-utils",
"actix-web",
"bitflags 2.4.1",
"bytes",
"derive_more",
"futures-core",
"http-range",
"log",
"mime",
"mime_guess",
"percent-encoding",
"pin-project-lite",
"v_htmlescape",
]
[[package]]
name = "actix-http"
version = "3.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d223b13fd481fc0d1f83bb12659ae774d9e3601814c68a0bc539731698cca743"
dependencies = [
"actix-codec",
"actix-rt",
"actix-service",
"actix-utils",
"ahash 0.8.8",
"base64 0.21.7",
"bitflags 2.4.1",
"brotli",
"bytes",
"bytestring",
"derive_more",
"encoding_rs",
"flate2",
"futures-core",
"h2 0.3.24",
"http 0.2.11",
"httparse",
"httpdate",
"itoa",
"language-tags",
"local-channel",
"mime",
"percent-encoding",
"pin-project-lite",
"rand",
"sha1",
"smallvec",
"tokio",
"tokio-util",
"tracing",
"zstd",
]
[[package]]
name = "actix-macros"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb"
dependencies = [
"quote",
"syn 2.0.48",
]
[[package]]
name = "actix-proxy"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3eb1ba1258bd79835d602dffb79f62aae42da7569036c67929e21961e2b3693c"
dependencies = [
"actix-web",
"awc",
"openssl",
]
[[package]]
name = "actix-router"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d22475596539443685426b6bdadb926ad0ecaefdfc5fb05e5e3441f15463c511"
dependencies = [
"bytestring",
"http 0.2.11",
"regex",
"serde",
"tracing",
]
[[package]]
name = "actix-rt"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28f32d40287d3f402ae0028a9d54bef51af15c8769492826a69d28f81893151d"
dependencies = [
"futures-core",
"tokio",
]
[[package]]
name = "actix-server"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3eb13e7eef0423ea6eab0e59f6c72e7cb46d33691ad56a726b3cd07ddec2c2d4"
dependencies = [
"actix-rt",
"actix-service",
"actix-utils",
"futures-core",
"futures-util",
"mio",
"socket2",
"tokio",
"tracing",
]
[[package]]
name = "actix-service"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b894941f818cfdc7ccc4b9e60fa7e53b5042a2e8567270f9147d5591893373a"
dependencies = [
"futures-core",
"paste",
"pin-project-lite",
]
[[package]]
name = "actix-tls"
version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4cce60a2f2b477bc72e5cde0af1812a6e82d8fd85b5570a5dcf2a5bf2c5be5f"
dependencies = [
"actix-rt",
"actix-service",
"actix-utils",
"futures-core",
"http 0.2.11",
"http 1.0.0",
"impl-more",
"openssl",
"pin-project-lite",
"tokio",
"tokio-openssl",
"tokio-util",
"tracing",
]
[[package]]
name = "actix-utils"
version = "3.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88a1dcdff1466e3c2488e1cb5c36a71822750ad43839937f85d2f4d9f8b705d8"
dependencies = [
"local-waker",
"pin-project-lite",
]
[[package]]
name = "actix-web"
version = "4.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43a6556ddebb638c2358714d853257ed226ece6023ef9364f23f0c70737ea984"
dependencies = [
"actix-codec",
"actix-http",
"actix-macros",
"actix-router",
"actix-rt",
"actix-server",
"actix-service",
"actix-utils",
"actix-web-codegen",
"ahash 0.8.8",
"bytes",
"bytestring",
"cfg-if",
"cookie 0.16.2",
"derive_more",
"encoding_rs",
"futures-core",
"futures-util",
"itoa",
"language-tags",
"log",
"mime",
"once_cell",
"pin-project-lite",
"regex",
"serde",
"serde_json",
"serde_urlencoded",
"smallvec",
"socket2",
"time",
"url",
]
[[package]]
name = "actix-web-codegen"
version = "4.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb1f50ebbb30eca122b188319a4398b3f7bb4a8cdf50ecfb73bfc6a3c3ce54f5"
dependencies = [
"actix-router",
"proc-macro2",
"quote",
"syn 2.0.48",
]
[[package]]
name = "addr2line"
version = "0.21.0"
@ -63,6 +297,19 @@ dependencies = [
"version_check",
]
[[package]]
name = "ahash"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42cd52102d3df161c77a887b608d7a4897d7cc112886a9537b738a887a03aaff"
dependencies = [
"cfg-if",
"getrandom",
"once_cell",
"version_check",
"zerocopy",
]
[[package]]
name = "aho-corasick"
version = "1.1.2"
@ -72,6 +319,21 @@ dependencies = [
"memchr",
]
[[package]]
name = "alloc-no-stdlib"
version = "2.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3"
[[package]]
name = "alloc-stdlib"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece"
dependencies = [
"alloc-no-stdlib",
]
[[package]]
name = "android-tzdata"
version = "0.1.1"
@ -104,6 +366,40 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "awc"
version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68c09cc97310b926f01621faee652f3d1b0962545a3cec6c9ac07def9ea36c2c"
dependencies = [
"actix-codec",
"actix-http",
"actix-rt",
"actix-service",
"actix-tls",
"actix-utils",
"base64 0.21.7",
"bytes",
"cfg-if",
"cookie 0.16.2",
"derive_more",
"futures-core",
"futures-util",
"h2 0.3.24",
"http 0.2.11",
"itoa",
"log",
"mime",
"openssl",
"percent-encoding",
"pin-project-lite",
"rand",
"serde",
"serde_json",
"serde_urlencoded",
"tokio",
]
[[package]]
name = "backtrace"
version = "0.3.69"
@ -152,6 +448,27 @@ dependencies = [
"generic-array",
]
[[package]]
name = "brotli"
version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "516074a47ef4bce09577a3b379392300159ce5b1ba2e501ff1c819950066100f"
dependencies = [
"alloc-no-stdlib",
"alloc-stdlib",
"brotli-decompressor",
]
[[package]]
name = "brotli-decompressor"
version = "2.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f"
dependencies = [
"alloc-no-stdlib",
"alloc-stdlib",
]
[[package]]
name = "bumpalo"
version = "3.14.0"
@ -170,12 +487,22 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
[[package]]
name = "bytestring"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74d80203ea6b29df88012294f62733de21cfeab47f17b41af3a38bc30a03ee72"
dependencies = [
"bytes",
]
[[package]]
name = "cc"
version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
dependencies = [
"jobserver",
"libc",
]
@ -232,6 +559,17 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
[[package]]
name = "cookie"
version = "0.16.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb"
dependencies = [
"percent-encoding",
"time",
"version_check",
]
[[package]]
name = "cookie"
version = "0.17.0"
@ -275,6 +613,15 @@ dependencies = [
"libc",
]
[[package]]
name = "crc32fast"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
dependencies = [
"cfg-if",
]
[[package]]
name = "crypto-common"
version = "0.1.6"
@ -406,6 +753,16 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
[[package]]
name = "flate2"
version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]]
name = "fnv"
version = "1.0.7"
@ -528,9 +885,9 @@ checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
[[package]]
name = "h2"
version = "0.3.23"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b553656127a00601c8ae5590fcfdc118e4083a7924b6cf4ffc1ea4b99dc429d7"
checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9"
dependencies = [
"bytes",
"fnv",
@ -570,7 +927,7 @@ version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
dependencies = [
"ahash",
"ahash 0.7.7",
]
[[package]]
@ -683,6 +1040,12 @@ dependencies = [
"pin-project-lite",
]
[[package]]
name = "http-range"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573"
[[package]]
name = "httparse"
version = "1.8.0"
@ -705,7 +1068,7 @@ dependencies = [
"futures-channel",
"futures-core",
"futures-util",
"h2 0.3.23",
"h2 0.3.24",
"http 0.2.11",
"http-body 0.4.6",
"httparse",
@ -814,6 +1177,12 @@ dependencies = [
"unicode-normalization",
]
[[package]]
name = "impl-more"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "206ca75c9c03ba3d4ace2460e57b189f39f43de612c2f85836e65c929701bb2d"
[[package]]
name = "indexmap"
version = "2.1.0"
@ -845,6 +1214,15 @@ version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
[[package]]
name = "jobserver"
version = "0.1.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6"
dependencies = [
"libc",
]
[[package]]
name = "js-sys"
version = "0.3.67"
@ -865,6 +1243,12 @@ dependencies = [
"serde",
]
[[package]]
name = "language-tags"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388"
[[package]]
name = "lazy_static"
version = "1.4.0"
@ -889,6 +1273,23 @@ version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456"
[[package]]
name = "local-channel"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6cbc85e69b8df4b8bb8b89ec634e7189099cea8927a276b7384ce5488e53ec8"
dependencies = [
"futures-core",
"futures-sink",
"local-waker",
]
[[package]]
name = "local-waker"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d873d7c67ce09b42110d801813efbc9364414e356be9935700d368351657487"
[[package]]
name = "lock_api"
version = "0.4.11"
@ -949,6 +1350,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09"
dependencies = [
"libc",
"log",
"wasi",
"windows-sys 0.48.0",
]
@ -1144,6 +1546,12 @@ dependencies = [
"windows-targets 0.48.5",
]
[[package]]
name = "paste"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
[[package]]
name = "pathdiff"
version = "0.2.1"
@ -1249,7 +1657,7 @@ dependencies = [
"base64 0.21.7",
"bytes",
"chrono",
"cookie",
"cookie 0.17.0",
"futures-util",
"headers",
"http 1.0.0",
@ -1495,7 +1903,7 @@ dependencies = [
"encoding_rs",
"futures-core",
"futures-util",
"h2 0.3.23",
"h2 0.3.24",
"http 0.2.11",
"http-body 0.4.6",
"hyper 0.14.28",
@ -1571,6 +1979,10 @@ dependencies = [
name = "roadsign"
version = "0.1.0"
dependencies = [
"actix-files",
"actix-proxy",
"actix-web",
"awc",
"config",
"futures-util",
"http 1.0.0",
@ -2032,6 +2444,18 @@ dependencies = [
"tokio",
]
[[package]]
name = "tokio-openssl"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ffab79df67727f6acf57f1ff743091873c24c579b1e2ce4d8f53e47ded4d63d"
dependencies = [
"futures-util",
"openssl",
"openssl-sys",
"tokio",
]
[[package]]
name = "tokio-stream"
version = "0.1.14"
@ -2318,6 +2742,12 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
[[package]]
name = "v_htmlescape"
version = "0.15.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e8257fbc510f0a46eb602c10215901938b5c2a7d5e70fc11483b1d3c9b5b18c"
[[package]]
name = "valuable"
version = "0.1.0"
@ -2623,3 +3053,51 @@ checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
dependencies = [
"linked-hash-map",
]
[[package]]
name = "zerocopy"
version = "0.7.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.7.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.48",
]
[[package]]
name = "zstd"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bffb3309596d527cfcba7dfc6ed6052f1d39dfbd7c867aa2e865e4a449c10110"
dependencies = [
"zstd-safe",
]
[[package]]
name = "zstd-safe"
version = "7.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43747c7422e2924c11144d5229878b98180ef8b06cca4ab5af37afc8a8d8ea3e"
dependencies = [
"zstd-sys",
]
[[package]]
name = "zstd-sys"
version = "2.0.9+zstd.1.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656"
dependencies = [
"cc",
"pkg-config",
]

View File

@ -6,6 +6,10 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
actix-files = "0.6.5"
actix-proxy = "0.2.0"
actix-web = "4.5.1"
awc = "3.4.0"
config = { version = "0.13.4", features = ["toml"] }
futures-util = "0.3.30"
http = "1.0.0"

98
certs/fullchain.pem Normal file
View File

@ -0,0 +1,98 @@
-----BEGIN CERTIFICATE-----
MIIGITCCBQmgAwIBAgISBPa2ahmRa5l0SrbR75K29TlqMA0GCSqGSIb3DQEBCwUA
MDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD
EwJSMzAeFw0yNDAxMjgxNjM1MzZaFw0yNDA0MjcxNjM1MzVaMBwxGjAYBgNVBAMT
EXNtYXJ0c2hlZXAuc3R1ZGlvMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC
AgEAumtDoEkBHiqBGNl9JUbt3Nw1a/iLa2JHZvCmUuilvafAj0k1UPEu457iJVjh
nxlTl/tgpCooRNviboB0lMAecwiKb5PIZzInY2/gb+rceL7rycalDAjWnwZZg8Tl
3JWJaU0D3+jeBQ9p7xkRPTcRpkGxFX9hRdInRqFGmCK4/OXrBwI3FkoP9Z8Nvgzc
RyIhmMEafOi2AaLJSXZQE508djHpcxlwoxvVmFIYsjTMJiq+zdqCZDTjDqt7bZC+
mp3n/DFLL0sTzIBLuHnPkrjwlE4w/XTLkMLgrGPvEJVHn4kOvnJSWJb95d3sRZqB
PLrekQGDtcb0dpFx8ctWycVp0qhBTpuLAoN+AK6cJ3IkxOcGm7sKzYwRINjAblMe
n1Y6as6JKL4zSZBt1jxua1NGIRQ1c4ReA78NniTMrFIk1mFL4kNT9ppgDsWGNTJx
FqC76rr3TdKXKzD/c8h57j3CPTF9lGB4Vmlkrq/zA++br0IG7Ki/+1n04Q1UDN0E
8z/vgUsT+pMItrmUhpnhb+4QWLQHTWOg1CyWT9xSm77ArkuNSdzNFGjpSbYZEPlh
pmAYTz+kWvnyg7T3Lt7fmSPCBnnIWhi9CJofvyomICWfcKEfwc0msJquXMyJBayA
K91VMoudbveI+CX8Zuc5u4uCOk/Dr6JJWnYkIDK7cW+tFh0CAwEAAaOCAkUwggJB
MA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw
DAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUPabqUNdBFv+3fEpgh2seAHvPQFgwHwYD
VR0jBBgwFoAUFC6zF7dYVsuuUAlA5h+vnYsUwsYwVQYIKwYBBQUHAQEESTBHMCEG
CCsGAQUFBzABhhVodHRwOi8vcjMuby5sZW5jci5vcmcwIgYIKwYBBQUHMAKGFmh0
dHA6Ly9yMy5pLmxlbmNyLm9yZy8wTQYDVR0RBEYwRIIaKi5wbGF5bWMuc21hcnRz
aGVlcC5zdHVkaW+CEyouc21hcnRzaGVlcC5zdHVkaW+CEXNtYXJ0c2hlZXAuc3R1
ZGlvMBMGA1UdIAQMMAowCAYGZ4EMAQIBMIIBBQYKKwYBBAHWeQIEAgSB9gSB8wDx
AHcAO1N3dT4tuYBOizBbBv5AO2fYT8P0x70ADS1yb+H61BcAAAGNUSQZ5gAABAMA
SDBGAiEA9i8QVPKh68OY9Ug+KqZ1aWSx5SVUvPKVyp8KkI/RMssCIQD0JsqwhLFx
SQFVROa1yhbZIdIHs9NMuW7lHbOWOU4F+AB2AO7N0GTV2xrOxVy3nbTNE6Iyh0Z8
vOzew1FIWUZxH7WbAAABjVEkGesAAAQDAEcwRQIhAKXGxcDa/aGK50QyhOXAdlKl
VCk0Yo9wYzw2sB48BF1TAiBsW2R+CJXhnCBzcRRh8GQuj+aBcYIzHBaRvsanC9nx
9zANBgkqhkiG9w0BAQsFAAOCAQEAgYR8uizQZkkvlEyI5dsz/tGJ0Vuejnd6yfoE
OT6BaNF45UtdMfSoJRLwgW6SM1hsuwmPjoUVS9VZ83NAhnWQqSeR9P4m7aMfhaeK
qPlDDcGh/SZOvgeja8AsUhCq+9csUzR+FEfk7xJXFflcG1FNGzRglzmqrSvFHKhE
EyJQuk6Oa/UiL0ICdKiLxh75gTJvTB/7wznTP7NGsayzqkBj4nxqHIwCGh7hPe2c
2xMEbcFA9aArInJEg2PzlYRy1C9qtuhU2hJUhLjEix4WVCHUyAV/X6kwjqIaUEOz
NdUrFCahGkWDVZZcPgl/FdFSPfSL/pnT8jOotELnhj0AiX2SNQ==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFFjCCAv6gAwIBAgIRAJErCErPDBinU/bWLiWnX1owDQYJKoZIhvcNAQELBQAw
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjAwOTA0MDAwMDAw
WhcNMjUwOTE1MTYwMDAwWjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg
RW5jcnlwdDELMAkGA1UEAxMCUjMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQC7AhUozPaglNMPEuyNVZLD+ILxmaZ6QoinXSaqtSu5xUyxr45r+XXIo9cP
R5QUVTVXjJ6oojkZ9YI8QqlObvU7wy7bjcCwXPNZOOftz2nwWgsbvsCUJCWH+jdx
sxPnHKzhm+/b5DtFUkWWqcFTzjTIUu61ru2P3mBw4qVUq7ZtDpelQDRrK9O8Zutm
NHz6a4uPVymZ+DAXXbpyb/uBxa3Shlg9F8fnCbvxK/eG3MHacV3URuPMrSXBiLxg
Z3Vms/EY96Jc5lP/Ooi2R6X/ExjqmAl3P51T+c8B5fWmcBcUr2Ok/5mzk53cU6cG
/kiFHaFpriV1uxPMUgP17VGhi9sVAgMBAAGjggEIMIIBBDAOBgNVHQ8BAf8EBAMC
AYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYB
Af8CAQAwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYfr52LFMLGMB8GA1UdIwQYMBaA
FHm0WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcw
AoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzAnBgNVHR8EIDAeMBygGqAYhhZodHRw
Oi8veDEuYy5sZW5jci5vcmcvMCIGA1UdIAQbMBkwCAYGZ4EMAQIBMA0GCysGAQQB
gt8TAQEBMA0GCSqGSIb3DQEBCwUAA4ICAQCFyk5HPqP3hUSFvNVneLKYY611TR6W
PTNlclQtgaDqw+34IL9fzLdwALduO/ZelN7kIJ+m74uyA+eitRY8kc607TkC53wl
ikfmZW4/RvTZ8M6UK+5UzhK8jCdLuMGYL6KvzXGRSgi3yLgjewQtCPkIVz6D2QQz
CkcheAmCJ8MqyJu5zlzyZMjAvnnAT45tRAxekrsu94sQ4egdRCnbWSDtY7kh+BIm
lJNXoB1lBMEKIq4QDUOXoRgffuDghje1WrG9ML+Hbisq/yFOGwXD9RiX8F6sw6W4
avAuvDszue5L3sz85K+EC4Y/wFVDNvZo4TYXao6Z0f+lQKc0t8DQYzk1OXVu8rp2
yJMC6alLbBfODALZvYH7n7do1AZls4I9d1P4jnkDrQoxB3UqQ9hVl3LEKQ73xF1O
yK5GhDDX8oVfGKF5u+decIsH4YaTw7mP3GFxJSqv3+0lUFJoi5Lc5da149p90Ids
hCExroL1+7mryIkXPeFM5TgO9r0rvZaBFOvV2z0gp35Z0+L4WPlbuEjN/lxPFin+
HlUjr8gRsI3qfJOQFy/9rKIJR0Y/8Omwt/8oTWgy1mdeHmmjk7j1nYsvC9JSQ6Zv
MldlTTKB3zhThV1+XWYp6rjd5JW1zbVWEkLNxE7GJThEUG3szgBVGP7pSWTUTsqX
nLRbwHOoq7hHwg==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFYDCCBEigAwIBAgIQQAF3ITfU6UK47naqPGQKtzANBgkqhkiG9w0BAQsFADA/
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
DkRTVCBSb290IENBIFgzMB4XDTIxMDEyMDE5MTQwM1oXDTI0MDkzMDE4MTQwM1ow
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwggIiMA0GCSqGSIb3DQEB
AQUAA4ICDwAwggIKAoICAQCt6CRz9BQ385ueK1coHIe+3LffOJCMbjzmV6B493XC
ov71am72AE8o295ohmxEk7axY/0UEmu/H9LqMZshftEzPLpI9d1537O4/xLxIZpL
wYqGcWlKZmZsj348cL+tKSIG8+TA5oCu4kuPt5l+lAOf00eXfJlII1PoOK5PCm+D
LtFJV4yAdLbaL9A4jXsDcCEbdfIwPPqPrt3aY6vrFk/CjhFLfs8L6P+1dy70sntK
4EwSJQxwjQMpoOFTJOwT2e4ZvxCzSow/iaNhUd6shweU9GNx7C7ib1uYgeGJXDR5
bHbvO5BieebbpJovJsXQEOEO3tkQjhb7t/eo98flAgeYjzYIlefiN5YNNnWe+w5y
sR2bvAP5SQXYgd0FtCrWQemsAXaVCg/Y39W9Eh81LygXbNKYwagJZHduRze6zqxZ
Xmidf3LWicUGQSk+WT7dJvUkyRGnWqNMQB9GoZm1pzpRboY7nn1ypxIFeFntPlF4
FQsDj43QLwWyPntKHEtzBRL8xurgUBN8Q5N0s8p0544fAQjQMNRbcTa0B7rBMDBc
SLeCO5imfWCKoqMpgsy6vYMEG6KDA0Gh1gXxG8K28Kh8hjtGqEgqiNx2mna/H2ql
PRmP6zjzZN7IKw0KKP/32+IVQtQi0Cdd4Xn+GOdwiK1O5tmLOsbdJ1Fu/7xk9TND
TwIDAQABo4IBRjCCAUIwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw
SwYIKwYBBQUHAQEEPzA9MDsGCCsGAQUFBzAChi9odHRwOi8vYXBwcy5pZGVudHJ1
c3QuY29tL3Jvb3RzL2RzdHJvb3RjYXgzLnA3YzAfBgNVHSMEGDAWgBTEp7Gkeyxx
+tvhS5B1/8QVYIWJEDBUBgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEB
ATAwMC4GCCsGAQUFBwIBFiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQu
b3JnMDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9jcmwuaWRlbnRydXN0LmNvbS9E
U1RST09UQ0FYM0NSTC5jcmwwHQYDVR0OBBYEFHm0WeZ7tuXkAXOACIjIGlj26Ztu
MA0GCSqGSIb3DQEBCwUAA4IBAQAKcwBslm7/DlLQrt2M51oGrS+o44+/yQoDFVDC
5WxCu2+b9LRPwkSICHXM6webFGJueN7sJ7o5XPWioW5WlHAQU7G75K/QosMrAdSW
9MUgNTP52GE24HGNtLi1qoJFlcDyqSMo59ahy2cI2qBDLKobkx/J3vWraV0T9VuG
WCLKTVXkcGdtwlfFRjlBz4pYg1htmf5X6DYO8A4jqv2Il9DjXA6USbW1FzXSLr9O
he8Y4IWS6wY7bCkjCWDcRQJMEhg76fsO3txE+FiYruq9RUWhiF1myv4Q6W+CyBFC
Dfvp7OOGAN6dEOM4+qR9sdjoSYKEBpsr6GtPAQw4dy753ec5
-----END CERTIFICATE-----

51
certs/privkey.pem Normal file
View File

@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

View File

@ -1,16 +1,14 @@
pub mod auth;
mod config;
mod proxies;
mod sideload;
pub mod warden;
use actix_web::{App, HttpServer, web};
use awc::Client;
use lazy_static::lazy_static;
use poem::{listener::TcpListener, EndpointExt, Route, Server};
use poem_openapi::OpenApiService;
use proxies::RoadInstance;
use tokio::sync::Mutex;
use tracing::{error, info, Level};
use crate::proxies::route;
lazy_static! {
@ -20,9 +18,6 @@ lazy_static! {
#[tokio::main]
async fn main() -> Result<(), std::io::Error> {
// Setting up logging
if std::env::var_os("RUST_LOG").is_none() {
std::env::set_var("RUST_LOG", "poem=debug");
}
tracing_subscriber::fmt()
.with_max_level(Level::DEBUG)
.init();
@ -44,36 +39,17 @@ async fn main() -> Result<(), std::io::Error> {
};
// Proxies
let proxies_server = Server::new(TcpListener::bind(
let proxies_server = HttpServer::new(|| {
App::new()
.app_data(web::Data::new(Client::default()))
.route("/", web::to(route::handle))
}).bind(
config::C
.read()
.await
.get_string("listen.proxies")
.unwrap_or("0.0.0.0:80".to_string()),
))
.run(route::handle);
// Sideload
let sideload = OpenApiService::new(sideload::SideloadApi, "Sideload API", "1.0")
.server("http://localhost:3000/cgi");
let sideload_server = Server::new(TcpListener::bind(
config::C
.read()
.await
.get_string("listen.sideload")
.unwrap_or("0.0.0.0:81".to_string()),
))
.run(
Route::new().nest("/cgi", sideload).with(auth::BasicAuth {
username: "RoadSign".to_string(),
password: config::C
.read()
.await
.get_string("secret")
.unwrap_or("password".to_string()),
}),
);
.unwrap_or("0.0.0.0:80".to_string())
)?.run();
// Process manager
{
@ -85,7 +61,7 @@ async fn main() -> Result<(), std::io::Error> {
app.warden.start().await;
}
tokio::try_join!(proxies_server, sideload_server)?;
tokio::try_join!(proxies_server)?;
Ok(())
}

View File

@ -1,52 +0,0 @@
use std::fmt::Write;
pub struct DirectoryTemplate<'a> {
pub path: &'a str,
pub files: Vec<FileRef>,
}
impl<'a> DirectoryTemplate<'a> {
pub fn render(&self) -> String {
let mut s = format!(
r#"
<html>
<head>
<title>Index of {}</title>
</head>
<body>
<h1>Index of /{}</h1>
<ul>"#,
self.path, self.path
);
for file in &self.files {
if file.is_dir {
let _ = write!(
s,
r#"<li><a href="{}">{}/</a></li>"#,
file.url, file.filename
);
} else {
let _ = write!(
s,
r#"<li><a href="{}">{}</a></li>"#,
file.url, file.filename
);
}
}
s.push_str(
r#"</ul>
</body>
</html>"#,
);
s
}
}
pub struct FileRef {
pub url: String,
pub filename: String,
pub is_dir: bool,
}

View File

@ -75,15 +75,6 @@ impl Destination {
.collect::<Vec<_>>()[0]
}
pub fn get_websocket_uri(&self) -> Result<String, ()> {
let parts = self.uri.as_str().splitn(2, "://").collect::<Vec<_>>();
let url = parts.get(1).unwrap_or(&"");
match self.get_protocol() {
"http" | "https" => Ok(url.replace("http", "ws")),
_ => Err(()),
}
}
pub fn get_hypertext_uri(&self) -> Result<String, ()> {
match self.get_protocol() {
"http" => Ok("http://".to_string() + self.get_host()),

View File

@ -1,5 +1,5 @@
use http::Method;
use poem::http::{HeaderMap, Uri};
use actix_web::http::header::HeaderMap;
use actix_web::http::{Method, Uri};
use regex::Regex;
use wildmatch::WildMatch;
@ -10,7 +10,6 @@ use self::{
metrics::RoadMetrics,
};
pub mod browser;
pub mod config;
pub mod loader;
pub mod metrics;
@ -38,7 +37,7 @@ impl RoadInstance {
pub fn filter(
&self,
uri: &Uri,
method: Method,
method: &Method,
headers: &HeaderMap,
) -> Option<(&Region, &Location)> {
self.regions.iter().find_map(|region| {

View File

@ -1,117 +1,50 @@
use futures_util::{SinkExt, StreamExt};
use http::{header, request::Builder, HeaderMap, Method, StatusCode, Uri};
use lazy_static::lazy_static;
use poem::{
web::{websocket::WebSocket, StaticFileRequest},
Body, Error, FromRequest, IntoResponse, Request, Response,
};
use futures_util::{SinkExt};
use std::{
ffi::OsStr,
path::{Path, PathBuf},
sync::Arc,
};
use tokio::sync::RwLock;
use tokio_tungstenite::connect_async;
use super::browser::{DirectoryTemplate, FileRef};
lazy_static! {
pub static ref CLIENT: reqwest::Client = reqwest::Client::new();
}
pub async fn repond_websocket(req: Builder, ws: WebSocket) -> Response {
ws.on_upgrade(move |socket| async move {
let (mut clientsink, mut clientstream) = socket.split();
// Start connection to server
let (serversocket, _) = connect_async(req.body(()).unwrap()).await.unwrap();
let (mut serversink, mut serverstream) = serversocket.split();
let client_live = Arc::new(RwLock::new(true));
let server_live = client_live.clone();
tokio::spawn(async move {
while let Some(Ok(msg)) = clientstream.next().await {
if (serversink.send(msg.into()).await).is_err() {
break;
};
if !*client_live.read().await {
break;
};
}
*client_live.write().await = false;
});
// Relay server messages to the client
tokio::spawn(async move {
while let Some(Ok(msg)) = serverstream.next().await {
if (clientsink.send(msg.into()).await).is_err() {
break;
};
if !*server_live.read().await {
break;
};
}
*server_live.write().await = false;
});
})
.into_response()
}
use actix_files::{NamedFile};
use actix_proxy::IntoHttpResponse;
use actix_web::{HttpRequest, HttpResponse, web};
use actix_web::http::Method;
use awc::Client;
pub async fn respond_hypertext(
uri: String,
ori: &Uri,
req: &Request,
method: Method,
body: Body,
headers: &HeaderMap,
) -> Result<Response, Error> {
let ip = req.remote_addr().to_string();
req: HttpRequest,
client: web::Data<Client>,
) -> Result<HttpResponse, HttpResponse> {
let ip = req.peer_addr().unwrap().ip().to_string();
let proto = req.uri().scheme_str().unwrap();
let host = req.uri().host().unwrap();
let mut headers = headers.clone();
headers.insert("Server", "RoadSign".parse().unwrap());
headers.insert("X-Forward-For", ip.parse().unwrap());
headers.insert("X-Forwarded-Proto", proto.parse().unwrap());
headers.insert("X-Forwarded-Host", host.parse().unwrap());
headers.insert("X-Real-IP", ip.parse().unwrap());
let mut headers = req.headers().clone();
headers.insert("Server".parse().unwrap(), "RoadSign".parse().unwrap());
headers.insert("X-Forward-For".parse().unwrap(), ip.parse().unwrap());
headers.insert("X-Forwarded-Proto".parse().unwrap(), proto.parse().unwrap());
headers.insert("X-Forwarded-Host".parse().unwrap(), host.parse().unwrap());
headers.insert("X-Real-IP".parse().unwrap(), ip.parse().unwrap());
headers.insert(
"Forwarded",
"Forwarded".parse().unwrap(),
format!("by={};for={};host={};proto={}", ip, ip, host, proto)
.parse()
.unwrap(),
);
let res = CLIENT
.request(method, uri + ori.path() + ori.query().unwrap_or(""))
.headers(headers.clone())
.body(body.into_bytes().await.unwrap())
.send()
.await;
let res = client.get(uri).send().await;
match res {
return match res {
Ok(result) => {
let mut res = Response::default();
res.extensions().clone_from(&result.extensions());
result.headers().iter().for_each(|(key, val)| {
res.headers_mut().insert(key, val.to_owned());
});
res.headers_mut()
.insert("Server", "RoadSign".parse().unwrap());
res.set_status(result.status());
res.set_version(result.version());
res.set_body(result.bytes().await.unwrap());
let mut res = result.into_http_response();
res.headers_mut().insert("Server".parse().unwrap(), "RoadSign".parse().unwrap());
Ok(res)
}
Err(error) => Err(Error::from_string(
error.to_string(),
error.status().unwrap_or(StatusCode::BAD_GATEWAY),
)),
Err(error) => {
Err(HttpResponse::BadGateway()
.body(format!("Something went wrong... {:}", error)))
}
};
}
pub struct StaticResponderConfig {
@ -126,14 +59,11 @@ pub struct StaticResponderConfig {
pub async fn respond_static(
cfg: StaticResponderConfig,
method: Method,
req: &Request,
) -> Result<Response, Error> {
if method != Method::GET {
return Err(Error::from_string(
"This destination only support GET request.",
StatusCode::METHOD_NOT_ALLOWED,
));
req: HttpRequest,
) -> Result<HttpResponse, HttpResponse> {
if req.method() != Method::GET {
return Err(HttpResponse::MethodNotAllowed()
.body("This destination only support GET request."));
}
let path = req
@ -142,9 +72,12 @@ pub async fn respond_static(
.trim_start_matches('/')
.trim_end_matches('/');
let path = percent_encoding::percent_decode_str(path)
.decode_utf8()
.map_err(|_| Error::from_status(StatusCode::NOT_FOUND))?;
let path = match percent_encoding::percent_decode_str(path).decode_utf8() {
Ok(val) => val,
Err(_) => {
return Err(HttpResponse::NotFound().body("Not found."));
}
};
let base_path = cfg.uri.parse::<PathBuf>().unwrap();
let mut file_path = base_path.clone();
@ -159,7 +92,8 @@ pub async fn respond_static(
}
if !file_path.starts_with(cfg.uri) {
return Err(Error::from_status(StatusCode::FORBIDDEN));
return Err(HttpResponse::Forbidden()
.body("Unexpected path."));
}
if !file_path.exists() {
@ -172,87 +106,30 @@ pub async fn respond_static(
file_path.pop();
file_path.push((file_name + &suffix).as_str());
if file_path.is_file() {
return Ok(StaticFileRequest::from_request_without_body(req)
.await?
.create_response(&file_path, cfg.utf8)?
.into_response());
return Ok(NamedFile::open(file_path).unwrap().into_response(&req));
}
}
if let Some(file) = cfg.fallback {
let fallback_path = base_path.join(file);
if fallback_path.is_file() {
return Ok(StaticFileRequest::from_request_without_body(req)
.await?
.create_response(&fallback_path, cfg.utf8)?
.into_response());
return Ok(NamedFile::open(fallback_path).unwrap().into_response(&req));
}
}
return Err(Error::from_status(StatusCode::NOT_FOUND));
}
if file_path.is_file() {
Ok(StaticFileRequest::from_request_without_body(req)
.await?
.create_response(&file_path, cfg.utf8)?
.into_response())
return Err(HttpResponse::NotFound().body("Not found."));
}
return if file_path.is_file() {
Ok(NamedFile::open(file_path).unwrap().into_response(&req))
} else {
if cfg.with_slash
&& !req.original_uri().path().ends_with('/')
&& (cfg.index.is_some() || cfg.browse)
{
let redirect_to = format!("{}/", req.original_uri().path());
return Ok(Response::builder()
.status(StatusCode::FOUND)
.header(header::LOCATION, redirect_to)
.finish());
}
if let Some(index_file) = &cfg.index {
let index_path = file_path.join(index_file);
if index_path.is_file() {
return Ok(StaticFileRequest::from_request_without_body(req)
.await?
.create_response(&index_path, cfg.utf8)?
.into_response());
return Ok(NamedFile::open(index_path).unwrap().into_response(&req));
}
}
if cfg.browse {
let read_dir = file_path
.read_dir()
.map_err(|_| Error::from_status(StatusCode::FORBIDDEN))?;
let mut template = DirectoryTemplate {
path: &path,
files: Vec::new(),
Err(HttpResponse::NotFound().body("Not found."))
};
for res in read_dir {
let entry = res.map_err(|_| Error::from_status(StatusCode::FORBIDDEN))?;
if let Some(filename) = entry.file_name().to_str() {
let mut base_url = req.original_uri().path().to_string();
if !base_url.ends_with('/') {
base_url.push('/');
}
let filename_url = percent_encoding::percent_encode(
filename.as_bytes(),
percent_encoding::NON_ALPHANUMERIC,
);
template.files.push(FileRef {
url: format!("{base_url}{filename_url}"),
filename: filename.to_string(),
is_dir: entry.path().is_dir(),
});
}
}
let html = template.render();
Ok(Response::builder()
.header(header::CONTENT_TYPE, mime::TEXT_HTML_UTF_8.as_ref())
.body(Body::from_string(html)))
} else {
Err(Error::from_status(StatusCode::NOT_FOUND))
}
}
}

View File

@ -1,10 +1,5 @@
use http::Method;
use poem::{
handler,
http::{HeaderMap, StatusCode, Uri},
web::websocket::WebSocket,
Body, Error, FromRequest, IntoResponse, Request, Response, Result,
};
use actix_web::{HttpRequest, HttpResponse, web};
use awc::Client;
use rand::seq::SliceRandom;
use crate::{
@ -15,22 +10,13 @@ use crate::{
ROAD,
};
#[handler]
pub async fn handle(
req: &Request,
uri: &Uri,
headers: &HeaderMap,
method: Method,
body: Body,
) -> Result<impl IntoResponse, Error> {
pub async fn handle(req: HttpRequest, client: web::Data<Client>) -> HttpResponse {
let readable_app = ROAD.lock().await;
let (region, location) = match readable_app.filter(uri, method.clone(), headers) {
let (region, location) = match readable_app.filter(req.uri(), req.method(), req.headers()) {
Some(val) => val,
None => {
return Err(Error::from_string(
"There are no region be able to respone this request.",
StatusCode::NOT_FOUND,
))
return HttpResponse::NotFound()
.body("There are no region be able to respone this request.");
}
};
@ -41,58 +27,31 @@ pub async fn handle(
async fn forward(
end: &Destination,
req: &Request,
ori: &Uri,
headers: &HeaderMap,
method: Method,
body: Body,
) -> Result<Response, Error> {
// Handle websocket
if let Ok(ws) = WebSocket::from_request_without_body(req).await {
// Get uri
let Ok(uri) = end.get_websocket_uri() else {
return Err(Error::from_string(
"This destination was not support websockets.",
StatusCode::NOT_IMPLEMENTED,
));
};
// Build request
let mut ws_req = http::Request::builder().uri(&uri);
for (key, value) in headers.iter() {
ws_req = ws_req.header(key, value);
}
// Start the websocket connection
return Ok(responder::repond_websocket(ws_req, ws).await);
}
req: HttpRequest,
client: web::Data<Client>,
) -> Result<HttpResponse, HttpResponse> {
// Handle normal web request
match end.get_type() {
DestinationType::Hypertext => {
let Ok(uri) = end.get_hypertext_uri() else {
return Err(Error::from_string(
"This destination was not support web requests.",
StatusCode::NOT_IMPLEMENTED,
));
return Err(HttpResponse::NotImplemented()
.body("This destination was not support web requests."));
};
responder::respond_hypertext(uri, ori, req, method, body, headers).await
responder::respond_hypertext(uri, req, client).await
}
DestinationType::StaticFiles => {
let Ok(cfg) = end.get_static_config() else {
return Err(Error::from_string(
"This destination was not support static files.",
StatusCode::NOT_IMPLEMENTED,
));
return Err(HttpResponse::NotImplemented()
.body("This destination was not support static files."));
};
responder::respond_static(cfg, method, req).await
responder::respond_static(cfg, req).await
}
_ => {
return Err(HttpResponse::NotImplemented()
.body("Unsupported destination protocol."));
}
_ => Err(Error::from_string(
"Unsupported destination protocol.",
StatusCode::NOT_IMPLEMENTED,
)),
}
}
@ -100,23 +59,22 @@ pub async fn handle(
let loc = location.clone();
let end = destination.clone();
match forward(&end, req, uri, headers, method, body).await {
return match forward(&end, req, client).await {
Ok(resp) => {
tokio::spawn(async move {
let writable_app = &mut ROAD.lock().await;
writable_app.metrics.add_success_request(reg, loc, end);
});
Ok(resp)
resp
}
Err(err) => {
let message = format!("{:}", err);
Err(resp) => {
tokio::spawn(async move {
let writable_app = &mut ROAD.lock().await;
writable_app
.metrics
.add_faliure_request(reg, loc, end, message);
.add_faliure_request(reg, loc, end, "TODO".to_owned());
});
Err(err)
resp
}
}
}

View File

@ -1,19 +0,0 @@
use poem_openapi::OpenApi;
pub mod overview;
pub mod regions;
pub struct SideloadApi;
#[OpenApi]
impl SideloadApi {
#[oai(path = "/", method = "get")]
async fn index(&self) -> overview::OverviewResponse {
overview::index().await
}
#[oai(path = "/regions", method = "get")]
async fn regions_index(&self) -> regions::RegionResponse {
regions::index().await
}
}

View File

@ -1,75 +0,0 @@
use poem_openapi::{payload::Json, ApiResponse, Object};
use crate::{
proxies::{
config::{Destination, Location},
metrics::RoadTrace,
},
ROAD,
};
#[derive(ApiResponse)]
pub enum OverviewResponse {
/// Return the overview data.
#[oai(status = 200)]
Ok(Json<OverviewData>),
}
#[derive(Debug, Object, Clone, PartialEq)]
pub struct OverviewData {
/// Loaded regions count
#[oai(read_only)]
regions: usize,
/// Loaded locations count
#[oai(read_only)]
locations: usize,
/// Loaded destnations count
#[oai(read_only)]
destinations: usize,
/// Recent requests count
requests_count: u64,
/// Recent requests success count
faliures_count: u64,
/// Recent requests falied count
successes_count: u64,
/// Recent requests success rate
success_rate: f64,
/// Recent successes
recent_successes: Vec<RoadTrace>,
/// Recent errors
recent_errors: Vec<RoadTrace>,
}
pub async fn index() -> OverviewResponse {
let locked_app = ROAD.lock().await;
let regions = locked_app.regions.clone();
let locations = regions
.iter()
.flat_map(|item| item.locations.clone())
.collect::<Vec<Location>>();
let destinations = locations
.iter()
.flat_map(|item| item.destinations.clone())
.collect::<Vec<Destination>>();
OverviewResponse::Ok(Json(OverviewData {
regions: regions.len(),
locations: locations.len(),
destinations: destinations.len(),
requests_count: locked_app.metrics.requests_count,
successes_count: locked_app.metrics.requests_count - locked_app.metrics.failures_count,
faliures_count: locked_app.metrics.failures_count,
success_rate: locked_app.metrics.get_success_rate(),
recent_successes: locked_app
.metrics
.recent_successes
.clone()
.into_iter()
.collect::<Vec<_>>(),
recent_errors: locked_app
.metrics
.recent_errors
.clone()
.into_iter()
.collect::<Vec<_>>(),
}))
}

View File

@ -1,25 +0,0 @@
use poem_openapi::{payload::Json, ApiResponse};
use crate::{proxies::config::Region, ROAD};
#[derive(ApiResponse)]
pub enum RegionResponse {
/// Return the region data.
#[oai(status = 200)]
Ok(Json<Region>),
/// Return the list of region data.
#[oai(status = 200)]
OkMany(Json<Vec<Region>>),
/// Return the region data after created.
#[oai(status = 201)]
Created(Json<Region>),
/// Return was not found.
#[oai(status = 404)]
NotFound,
}
pub async fn index() -> RegionResponse {
let locked_app = ROAD.lock().await;
RegionResponse::OkMany(Json(locked_app.regions.clone()))
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
:root{--bs-body-font-family: "IBM Plex Serif", "Noto Serif SC", sans-serif !important}html,body{font-family:var(--bs-body-font-family)}@font-face{font-display:swap;font-family:"IBM Plex Serif";font-style:normal;font-weight:100;src:url(/_astro/ibm-plex-serif-v19-latin-100.6qNbweSL.woff2) format("woff2")}@font-face{font-display:swap;font-family:"IBM Plex Serif";font-style:italic;font-weight:100;src:url(/_astro/ibm-plex-serif-v19-latin-100italic.E22nrI7z.woff2) format("woff2")}@font-face{font-display:swap;font-family:"IBM Plex Serif";font-style:normal;font-weight:200;src:url(/_astro/ibm-plex-serif-v19-latin-200.GFXE_YJc.woff2) format("woff2")}@font-face{font-display:swap;font-family:"IBM Plex Serif";font-style:italic;font-weight:200;src:url(/_astro/ibm-plex-serif-v19-latin-200italic.pJK4yaaG.woff2) format("woff2")}@font-face{font-display:swap;font-family:"IBM Plex Serif";font-style:normal;font-weight:300;src:url(/_astro/ibm-plex-serif-v19-latin-300.RVbRgkxX.woff2) format("woff2")}@font-face{font-display:swap;font-family:"IBM Plex Serif";font-style:italic;font-weight:300;src:url(/_astro/ibm-plex-serif-v19-latin-300italic.ZdSVgmcR.woff2) format("woff2")}@font-face{font-display:swap;font-family:"IBM Plex Serif";font-style:normal;font-weight:400;src:url(/_astro/ibm-plex-serif-v19-latin-regular.HRmMD3sQ.woff2) format("woff2")}@font-face{font-display:swap;font-family:"IBM Plex Serif";font-style:italic;font-weight:400;src:url(/_astro/ibm-plex-serif-v19-latin-italic.MiJiQVsi.woff2) format("woff2")}@font-face{font-display:swap;font-family:"IBM Plex Serif";font-style:normal;font-weight:500;src:url(/_astro/ibm-plex-serif-v19-latin-500.xAA_w-Ac.woff2) format("woff2")}@font-face{font-display:swap;font-family:"IBM Plex Serif";font-style:italic;font-weight:500;src:url(/_astro/ibm-plex-serif-v19-latin-500italic.Unq84pJ7.woff2) format("woff2")}@font-face{font-display:swap;font-family:"IBM Plex Serif";font-style:normal;font-weight:600;src:url(/_astro/ibm-plex-serif-v19-latin-600.cuuqzllG.woff2) format("woff2")}@font-face{font-display:swap;font-family:"IBM Plex Serif";font-style:italic;font-weight:600;src:url(/_astro/ibm-plex-serif-v19-latin-600italic.vDhUog1q.woff2) format("woff2")}@font-face{font-display:swap;font-family:"IBM Plex Serif";font-style:normal;font-weight:700;src:url(/_astro/ibm-plex-serif-v19-latin-700.yX9JjmCp.woff2) format("woff2")}@font-face{font-display:swap;font-family:"IBM Plex Serif";font-style:italic;font-weight:700;src:url(/_astro/ibm-plex-serif-v19-latin-700italic.QM1RA0vx.woff2) format("woff2")}@font-face{font-display:swap;font-family:"Noto Serif SC";font-style:normal;font-weight:200;src:url(/_astro/noto-serif-sc-v22-chinese-simplified-200.g4OBZhIi.woff2) format("woff2")}@font-face{font-display:swap;font-family:"Noto Serif SC";font-style:normal;font-weight:300;src:url(/_astro/noto-serif-sc-v22-chinese-simplified-300.yFtdUYoh.woff2) format("woff2")}@font-face{font-display:swap;font-family:"Noto Serif SC";font-style:normal;font-weight:400;src:url(/_astro/noto-serif-sc-v22-chinese-simplified-regular.9muiKgFz.woff2) format("woff2")}@font-face{font-display:swap;font-family:"Noto Serif SC";font-style:normal;font-weight:500;src:url(/_astro/noto-serif-sc-v22-chinese-simplified-500.exkAspFQ.woff2) format("woff2")}@font-face{font-display:swap;font-family:"Noto Serif SC";font-style:normal;font-weight:600;src:url(/_astro/noto-serif-sc-v22-chinese-simplified-600.4n6uFOXj.woff2) format("woff2")}@font-face{font-display:swap;font-family:"Noto Serif SC";font-style:normal;font-weight:700;src:url(/_astro/noto-serif-sc-v22-chinese-simplified-700.HyiB9Pzv.woff2) format("woff2")}@font-face{font-display:swap;font-family:"Noto Serif SC";font-style:normal;font-weight:900;src:url(/_astro/noto-serif-sc-v22-chinese-simplified-900.ERSRy_0V.woff2) format("woff2")}.astro-route-announcer{position:absolute;left:0;top:0;clip:rect(0 0 0 0);-webkit-clip-path:inset(50%);clip-path:inset(50%);overflow:hidden;white-space:nowrap;width:1px;height:1px}.h-fullpage{height:calc(100vh - 64px)}.max-h-fullpage{max-height:calc(100vh - 64px)}.mt-header{margin-top:64px}.top-header{top:64px}html{overflow-x:hidden!important;overflow-y:auto!important}@keyframes astroFadeInOut{0%{opacity:1}to{opacity:0}}@keyframes astroFadeIn{0%{opacity:0}}@keyframes astroFadeOut{to{opacity:0}}@keyframes astroSlideFromRight{0%{transform:translate(100%)}}@keyframes astroSlideFromLeft{0%{transform:translate(-100%)}}@keyframes astroSlideToRight{to{transform:translate(100%)}}@keyframes astroSlideToLeft{to{transform:translate(-100%)}}@media (prefers-reduced-motion){::view-transition-group(*),::view-transition-old(*),::view-transition-new(*){animation:none!important}[data-astro-transition-scope]{animation:none!important}}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,2 @@
index.html
assets/

View File

@ -0,0 +1,21 @@
<svg version="1.2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" width="1024" height="1024">
<title>SmartSheep Logo</title>
<defs>
<image width="124" height="198" id="img1" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHwAAADGCAMAAAAnkRSfAAAAAXNSR0IB2cksfwAAAq9QTFRFAAAA////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////J9QBRAAAAOV0Uk5TAA0WEiFfjMLMr40/BRdmtuz//vbQYAQmpfLdQAGovyURj/iOD0r07loDGJfbPDTf/bhd94QLdPrrVQLWcPmtHU31egknzehRg9Ev1xnzbgYHhfzmTMorSOSbFJloKsniReXDKG/7kQ6m8WMw3ue7I4cKNdTZOFTqsR5/Hzra0zZY7al4Ir5SPWHvnpbG4MRrlKJs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"/>
<image width="122" height="142" id="img2" href="data:image/png;base64,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"/>
</defs>
<style>
.s0 { fill: #ffffff;stroke: #000000;stroke-miterlimit:100;stroke-width: 56 }
.s1 { fill: #4750a3;stroke: #000000;stroke-miterlimit:100;stroke-width: 56 }
</style>
<path id="Wool" fill-rule="evenodd" class="s0" d="m128 608.4c0 95.9 77.4 173.6 172.8 173.6h441.6c84.8 0 153.6-69.1 153.6-154.3 0-74.6-52.8-136.9-122.9-151.1 4.9-12.9 7.7-27 7.7-41.7 0-63.9-51.6-115.8-115.2-115.8-23.6 0-45.7 7.3-64 19.6-33.2-57.9-95.2-96.7-166.4-96.7-106.1 0-192 86.3-192 192.9 0 3.2 0.1 6.5 0.2 9.7-67.2 23.8-115.4 88.1-115.4 163.8z"/>
<g id="Crystal">
<path id="Crystal" class="s1" d="m699 224l138.6 80v160l-138.6 80-138.6-80v-160z"/>
<use id="Highlight" href="#img1" x="688" y="255"/>
</g>
<g id="Horn">
</g>
<g id="Face">
<use id="Slime" href="#img2" x="233" y="538"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

View File

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1"><url><loc>https://smartsheep.studio/</loc></url><url><loc>https://smartsheep.studio/events/</loc></url><url><loc>https://smartsheep.studio/posts/</loc></url></urlset>

View File

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"><sitemap><loc>https://smartsheep.studio/sitemap-0.xml</loc></sitemap></sitemapindex>

View File

@ -0,0 +1,3 @@
const onRequest = undefined;
export { onRequest };

View File

@ -0,0 +1,6 @@
export { renderers } from '../renderers.mjs';
export { onRequest } from '../_empty-middleware.mjs';
const page = () => import('./pages/_slug__TUDhKBhQ.mjs').then(n => n.c);
export { page };

View File

@ -0,0 +1,6 @@
export { renderers } from '../renderers.mjs';
export { onRequest } from '../_empty-middleware.mjs';
const page = () => import('./pages/_slug__TUDhKBhQ.mjs').then(n => n._);
export { page };

View File

@ -0,0 +1,6 @@
export { renderers } from '../renderers.mjs';
export { onRequest } from '../_empty-middleware.mjs';
const page = () => import('./pages/_slug__TUDhKBhQ.mjs').then(n => n.d);
export { page };

View File

@ -0,0 +1,342 @@
import { isRemotePath, joinPaths } from '@astrojs/internal-helpers/path';
import { A as AstroError, E as ExpectedImage, L as LocalImageUsedWrongly, M as MissingImageDimension, U as UnsupportedImageFormat, I as IncompatibleDescriptorOptions, a as UnsupportedImageConversion, b as MissingSharp } from '../astro_5WdVqH1c.mjs';
const VALID_SUPPORTED_FORMATS = [
"jpeg",
"jpg",
"png",
"tiff",
"webp",
"gif",
"svg",
"avif"
];
const DEFAULT_OUTPUT_FORMAT = "webp";
const DEFAULT_HASH_PROPS = ["src", "width", "height", "format", "quality"];
function isESMImportedImage(src) {
return typeof src === "object";
}
function isRemoteImage(src) {
return typeof src === "string";
}
function matchPattern(url, remotePattern) {
return matchProtocol(url, remotePattern.protocol) && matchHostname(url, remotePattern.hostname, true) && matchPort(url, remotePattern.port) && matchPathname(url, remotePattern.pathname, true);
}
function matchPort(url, port) {
return !port || port === url.port;
}
function matchProtocol(url, protocol) {
return !protocol || protocol === url.protocol.slice(0, -1);
}
function matchHostname(url, hostname, allowWildcard) {
if (!hostname) {
return true;
} else if (!allowWildcard || !hostname.startsWith("*")) {
return hostname === url.hostname;
} else if (hostname.startsWith("**.")) {
const slicedHostname = hostname.slice(2);
return slicedHostname !== url.hostname && url.hostname.endsWith(slicedHostname);
} else if (hostname.startsWith("*.")) {
const slicedHostname = hostname.slice(1);
const additionalSubdomains = url.hostname.replace(slicedHostname, "").split(".").filter(Boolean);
return additionalSubdomains.length === 1;
}
return false;
}
function matchPathname(url, pathname, allowWildcard) {
if (!pathname) {
return true;
} else if (!allowWildcard || !pathname.endsWith("*")) {
return pathname === url.pathname;
} else if (pathname.endsWith("/**")) {
const slicedPathname = pathname.slice(0, -2);
return slicedPathname !== url.pathname && url.pathname.startsWith(slicedPathname);
} else if (pathname.endsWith("/*")) {
const slicedPathname = pathname.slice(0, -1);
const additionalPathChunks = url.pathname.replace(slicedPathname, "").split("/").filter(Boolean);
return additionalPathChunks.length === 1;
}
return false;
}
function isRemoteAllowed(src, {
domains = [],
remotePatterns = []
}) {
if (!isRemotePath(src))
return false;
const url = new URL(src);
return domains.some((domain) => matchHostname(url, domain)) || remotePatterns.some((remotePattern) => matchPattern(url, remotePattern));
}
function isLocalService(service) {
if (!service) {
return false;
}
return "transform" in service;
}
function parseQuality(quality) {
let result = parseInt(quality);
if (Number.isNaN(result)) {
return quality;
}
return result;
}
const baseService = {
propertiesToHash: DEFAULT_HASH_PROPS,
validateOptions(options) {
if (!options.src || typeof options.src !== "string" && typeof options.src !== "object") {
throw new AstroError({
...ExpectedImage,
message: ExpectedImage.message(
JSON.stringify(options.src),
typeof options.src,
JSON.stringify(options, (_, v) => v === void 0 ? null : v)
)
});
}
if (!isESMImportedImage(options.src)) {
if (options.src.startsWith("/@fs/") || !isRemotePath(options.src) && !options.src.startsWith("/")) {
throw new AstroError({
...LocalImageUsedWrongly,
message: LocalImageUsedWrongly.message(options.src)
});
}
let missingDimension;
if (!options.width && !options.height) {
missingDimension = "both";
} else if (!options.width && options.height) {
missingDimension = "width";
} else if (options.width && !options.height) {
missingDimension = "height";
}
if (missingDimension) {
throw new AstroError({
...MissingImageDimension,
message: MissingImageDimension.message(missingDimension, options.src)
});
}
} else {
if (!VALID_SUPPORTED_FORMATS.includes(options.src.format)) {
throw new AstroError({
...UnsupportedImageFormat,
message: UnsupportedImageFormat.message(
options.src.format,
options.src.src,
VALID_SUPPORTED_FORMATS
)
});
}
if (options.widths && options.densities) {
throw new AstroError(IncompatibleDescriptorOptions);
}
if (options.src.format === "svg") {
options.format = "svg";
}
if (options.src.format === "svg" && options.format !== "svg" || options.src.format !== "svg" && options.format === "svg") {
throw new AstroError(UnsupportedImageConversion);
}
}
if (!options.format) {
options.format = DEFAULT_OUTPUT_FORMAT;
}
if (options.width)
options.width = Math.round(options.width);
if (options.height)
options.height = Math.round(options.height);
return options;
},
getHTMLAttributes(options) {
const { targetWidth, targetHeight } = getTargetDimensions(options);
const { src, width, height, format, quality, densities, widths, formats, ...attributes } = options;
return {
...attributes,
width: targetWidth,
height: targetHeight,
loading: attributes.loading ?? "lazy",
decoding: attributes.decoding ?? "async"
};
},
getSrcSet(options) {
const srcSet = [];
const { targetWidth } = getTargetDimensions(options);
const { widths, densities } = options;
const targetFormat = options.format ?? DEFAULT_OUTPUT_FORMAT;
let imageWidth = options.width;
let maxWidth = Infinity;
if (isESMImportedImage(options.src)) {
imageWidth = options.src.width;
maxWidth = imageWidth;
}
const {
width: transformWidth,
height: transformHeight,
...transformWithoutDimensions
} = options;
const allWidths = [];
if (densities) {
const densityValues = densities.map((density) => {
if (typeof density === "number") {
return density;
} else {
return parseFloat(density);
}
});
const densityWidths = densityValues.sort().map((density) => Math.round(targetWidth * density));
allWidths.push(
...densityWidths.map((width, index) => ({
maxTargetWidth: Math.min(width, maxWidth),
descriptor: `${densityValues[index]}x`
}))
);
} else if (widths) {
allWidths.push(
...widths.map((width) => ({
maxTargetWidth: Math.min(width, maxWidth),
descriptor: `${width}w`
}))
);
}
for (const { maxTargetWidth, descriptor } of allWidths) {
const srcSetTransform = { ...transformWithoutDimensions };
if (maxTargetWidth !== imageWidth) {
srcSetTransform.width = maxTargetWidth;
} else {
if (options.width && options.height) {
srcSetTransform.width = options.width;
srcSetTransform.height = options.height;
}
}
srcSet.push({
transform: srcSetTransform,
descriptor,
attributes: {
type: `image/${targetFormat}`
}
});
}
return srcSet;
},
getURL(options, imageConfig) {
const searchParams = new URLSearchParams();
if (isESMImportedImage(options.src)) {
searchParams.append("href", options.src.src);
} else if (isRemoteAllowed(options.src, imageConfig)) {
searchParams.append("href", options.src);
} else {
return options.src;
}
const params = {
w: "width",
h: "height",
q: "quality",
f: "format"
};
Object.entries(params).forEach(([param, key]) => {
options[key] && searchParams.append(param, options[key].toString());
});
const imageEndpoint = joinPaths("/", "/_image");
return `${imageEndpoint}?${searchParams}`;
},
parseURL(url) {
const params = url.searchParams;
if (!params.has("href")) {
return void 0;
}
const transform = {
src: params.get("href"),
width: params.has("w") ? parseInt(params.get("w")) : void 0,
height: params.has("h") ? parseInt(params.get("h")) : void 0,
format: params.get("f"),
quality: params.get("q")
};
return transform;
}
};
function getTargetDimensions(options) {
let targetWidth = options.width;
let targetHeight = options.height;
if (isESMImportedImage(options.src)) {
const aspectRatio = options.src.width / options.src.height;
if (targetHeight && !targetWidth) {
targetWidth = Math.round(targetHeight * aspectRatio);
} else if (targetWidth && !targetHeight) {
targetHeight = Math.round(targetWidth / aspectRatio);
} else if (!targetWidth && !targetHeight) {
targetWidth = options.src.width;
targetHeight = options.src.height;
}
}
return {
targetWidth,
targetHeight
};
}
let sharp;
const qualityTable = {
low: 25,
mid: 50,
high: 80,
max: 100
};
async function loadSharp() {
let sharpImport;
try {
sharpImport = (await import('sharp')).default;
} catch (e) {
throw new AstroError(MissingSharp);
}
return sharpImport;
}
const sharpService = {
validateOptions: baseService.validateOptions,
getURL: baseService.getURL,
parseURL: baseService.parseURL,
getHTMLAttributes: baseService.getHTMLAttributes,
getSrcSet: baseService.getSrcSet,
async transform(inputBuffer, transformOptions, config) {
if (!sharp)
sharp = await loadSharp();
const transform = transformOptions;
if (transform.format === "svg")
return { data: inputBuffer, format: "svg" };
const result = sharp(inputBuffer, {
failOnError: false,
pages: -1,
limitInputPixels: config.service.config.limitInputPixels
});
result.rotate();
if (transform.height && !transform.width) {
result.resize({ height: Math.round(transform.height) });
} else if (transform.width) {
result.resize({ width: Math.round(transform.width) });
}
if (transform.format) {
let quality = void 0;
if (transform.quality) {
const parsedQuality = parseQuality(transform.quality);
if (typeof parsedQuality === "number") {
quality = parsedQuality;
} else {
quality = transform.quality in qualityTable ? qualityTable[transform.quality] : void 0;
}
}
result.toFormat(transform.format, { quality });
}
const { data, info } = await result.toBuffer({ resolveWithObject: true });
return {
data,
format: info.format
};
}
};
var sharp_default = sharpService;
const sharp$1 = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
__proto__: null,
default: sharp_default
}, Symbol.toStringTag, { value: 'Module' }));
export { DEFAULT_HASH_PROPS as D, isLocalService as a, isRemoteImage as b, isRemoteAllowed as c, isESMImportedImage as i, sharp$1 as s };

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,6 @@
export { renderers } from '../renderers.mjs';
export { onRequest } from '../_empty-middleware.mjs';
const page = () => import('./pages/index_l5vwnKzb.mjs').then(n => n.b);
export { page };

View File

@ -0,0 +1,6 @@
export { renderers } from '../renderers.mjs';
export { onRequest } from '../_empty-middleware.mjs';
const page = () => import('./pages/index_l5vwnKzb.mjs').then(n => n.i);
export { page };

View File

@ -0,0 +1,6 @@
export { renderers } from '../renderers.mjs';
export { onRequest } from '../_empty-middleware.mjs';
const page = () => import('./pages/index_l5vwnKzb.mjs').then(n => n.a);
export { page };

View File

@ -0,0 +1,6 @@
export { renderers } from '../renderers.mjs';
export { onRequest } from '../_empty-middleware.mjs';
const page = () => import('./pages/node_hIg2I-Kh.mjs');
export { page };

View File

@ -0,0 +1,245 @@
/* empty css */
import 'html-escaper';
import { c as createAstro, d as createComponent, r as renderTemplate, m as maybeRenderHead, e as addAttribute, f as renderSlot, g as renderTransition, h as renderComponent, i as renderHead } from '../astro_5WdVqH1c.mjs';
import 'kleur/colors';
import 'clsx';
import { DocumentRenderer } from '@keystone-6/document-renderer';
/* empty css */
/* empty css */
const $$Astro$7 = createAstro("https://smartsheep.studio");
const $$Navbar = createComponent(async ($$result, $$props, $$slots) => {
const Astro2 = $$result.createAstro($$Astro$7, $$props, $$slots);
Astro2.self = $$Navbar;
const items = [
{
label: "\u60C5\u62A5",
children: [
{ href: "/posts", label: "\u8BB0\u5F55" },
{ href: "/events", label: "\u6D3B\u52A8" }
]
}
];
return renderTemplate`${maybeRenderHead()}<div class="fixed top-0 navbar shadow-md bg-base-100 lg:px-5 z-10"> <div class="navbar-start"> <div class="dropdown"> <div tabindex="0" role="button" class="btn btn-ghost lg:hidden"> <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h8m-8 6h16"></path> </svg> </div> <ul tabindex="0" class="menu menu-sm dropdown-content mt-3 z-[1] p-2 shadow bg-base-100 rounded-box w-52"> ${items.map((item) => renderTemplate`<li> <a${addAttribute(item.href, "href")}>${item.label}</a> ${item.children && renderTemplate`<ul class="p-2"> ${item.children?.map((child) => renderTemplate`<li> <a${addAttribute(child.href, "href")}>${child.label}</a> </li>`)} </ul>`} </li>`)} </ul> </div> <a class="btn btn-ghost text-xl" href="/">山羊寒舍</a> </div> <div class="navbar-center hidden lg:flex"> <ul class="menu menu-horizontal px-1"> ${items.map((item) => renderTemplate`<li> ${item.children ? renderTemplate`<details> <summary>${item.label}</summary> <ul class="p-2"> ${item.children?.map((child) => renderTemplate`<li> <a${addAttribute(child.href, "href")}>${child.label}</a> </li>`)} </ul> </details>` : renderTemplate`<a${addAttribute(item.href, "href")}>${item.label}</a>`} </li>`)} </ul> </div> <div class="navbar-end"> <label class="swap swap-rotate px-[16px]"> <input type="checkbox" class="theme-controller" value="light" checked> <svg class="swap-on fill-current w-8 h-8" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> <path d="M5.64,17l-.71.71a1,1,0,0,0,0,1.41,1,1,0,0,0,1.41,0l.71-.71A1,1,0,0,0,5.64,17ZM5,12a1,1,0,0,0-1-1H3a1,1,0,0,0,0,2H4A1,1,0,0,0,5,12Zm7-7a1,1,0,0,0,1-1V3a1,1,0,0,0-2,0V4A1,1,0,0,0,12,5ZM5.64,7.05a1,1,0,0,0,.7.29,1,1,0,0,0,.71-.29,1,1,0,0,0,0-1.41l-.71-.71A1,1,0,0,0,4.93,6.34Zm12,.29a1,1,0,0,0,.7-.29l.71-.71a1,1,0,1,0-1.41-1.41L17,5.64a1,1,0,0,0,0,1.41A1,1,0,0,0,17.66,7.34ZM21,11H20a1,1,0,0,0,0,2h1a1,1,0,0,0,0-2Zm-9,8a1,1,0,0,0-1,1v1a1,1,0,0,0,2,0V20A1,1,0,0,0,12,19ZM18.36,17A1,1,0,0,0,17,18.36l.71.71a1,1,0,0,0,1.41,0,1,1,0,0,0,0-1.41ZM12,6.5A5.5,5.5,0,1,0,17.5,12,5.51,5.51,0,0,0,12,6.5Zm0,9A3.5,3.5,0,1,1,15.5,12,3.5,3.5,0,0,1,12,15.5Z"></path> </svg> <svg class="swap-off fill-current w-8 h-8" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> <path d="M21.64,13a1,1,0,0,0-1.05-.14,8.05,8.05,0,0,1-3.37.73A8.15,8.15,0,0,1,9.08,5.49a8.59,8.59,0,0,1,.25-2A1,1,0,0,0,8,2.36,10.14,10.14,0,1,0,22,14.05,1,1,0,0,0,21.64,13Zm-9.5,6.69A8.14,8.14,0,0,1,7.08,5.22v.27A10.15,10.15,0,0,0,17.22,15.63a9.79,9.79,0,0,0,2.1-.22A8.11,8.11,0,0,1,12.14,19.73Z"></path> </svg> </label> </div> </div>`;
}, "/Users/littlesheep/Documents/Projects/Capital/src/components/Navbar.astro", void 0);
const $$Astro$6 = createAstro("https://smartsheep.studio");
const $$ViewTransitions = createComponent(async ($$result, $$props, $$slots) => {
const Astro2 = $$result.createAstro($$Astro$6, $$props, $$slots);
Astro2.self = $$ViewTransitions;
const { fallback = "animate" } = Astro2.props;
return renderTemplate`<meta name="astro-view-transitions-enabled" content="true"><meta name="astro-view-transitions-fallback"${addAttribute(fallback, "content")}>`;
}, "/Users/littlesheep/Documents/Projects/Capital/node_modules/astro/components/ViewTransitions.astro", void 0);
var __freeze = Object.freeze;
var __defProp = Object.defineProperty;
var __template = (cooked, raw) => __freeze(__defProp(cooked, "raw", { value: __freeze(raw || cooked.slice()) }));
var _a;
const $$Astro$5 = createAstro("https://smartsheep.studio");
const $$RootLayout = createComponent(async ($$result, $$props, $$slots) => {
const Astro2 = $$result.createAstro($$Astro$5, $$props, $$slots);
Astro2.self = $$RootLayout;
const { title } = Astro2.props;
return renderTemplate(_a || (_a = __template(['<html lang="en" data-astro-cid-mdysn4oi> <head><meta charset="utf-8"><link rel="icon" type="image/svg+xml" href="/favicon.svg"><meta name="viewport" content="width=device-width"><meta name="generator"', ">", "", "", "", "</head> <body data-astro-cid-mdysn4oi> <!-- Header --> ", " <!-- Content --> <main data-astro-cid-mdysn4oi", "> ", ' </main> <!-- Styles --> <script async src="https://analytics.smartsheep.studio/script.js" data-website-id="9d676a27-b473-44a3-b444-5a7d851e31e8"><\/script> </body> </html>'])), addAttribute(Astro2.generator, "content"), title && renderTemplate`<title>山羊寒舍 | ${title}</title>`, !title && renderTemplate`<title>山羊寒舍</title>`, renderComponent($$result, "ViewTransitions", $$ViewTransitions, { "data-astro-cid-mdysn4oi": true }), renderHead(), renderComponent($$result, "Navbar", $$Navbar, { "data-astro-cid-mdysn4oi": true }), addAttribute(renderTransition($$result, "53mar5bf", "slide", ""), "data-astro-transition-scope"), renderSlot($$result, $$slots["default"]));
}, "/Users/littlesheep/Documents/Projects/Capital/src/layouts/RootLayout.astro", "self");
const $$Astro$4 = createAstro("https://smartsheep.studio");
const $$PageLayout = createComponent(async ($$result, $$props, $$slots) => {
const Astro2 = $$result.createAstro($$Astro$4, $$props, $$slots);
Astro2.self = $$PageLayout;
const { title } = Astro2.props;
return renderTemplate`${renderComponent($$result, "RootLayout", $$RootLayout, { "title": title }, { "default": ($$result2) => renderTemplate` ${maybeRenderHead()}<main class="container mx-auto h-fullpage mt-header"> ${renderSlot($$result2, $$slots["default"])} </main> ` })}`;
}, "/Users/littlesheep/Documents/Projects/Capital/src/layouts/PageLayout.astro", void 0);
const defaultCms = "https://smartsheep.studio";
async function graphQuery(query, variables) {
const response = await fetch(`${process.env.PUBLIC_CMS ?? defaultCms}/api/graphql`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
query,
variables
})
});
return await response.json();
}
const POST_TYPES = {
article: "文章",
podcast: "播客",
announcements: "通告"
};
const $$Astro$3 = createAstro("https://smartsheep.studio");
const $$PostList = createComponent(async ($$result, $$props, $$slots) => {
const Astro2 = $$result.createAstro($$Astro$3, $$props, $$slots);
Astro2.self = $$PostList;
const { posts } = Astro2.props;
return renderTemplate`${maybeRenderHead()}<div class="grid justify-items-strench shadow-lg"> ${posts?.map((item) => renderTemplate`<a${addAttribute(`/p/${item.slug}`, "href")}> <div class="card sm:card-side hover:bg-base-200 transition-colors sm:max-w-none"> ${item.cover.image.url && renderTemplate`<figure class="mx-auto w-full object-cover p-6 max-sm:pb-0 sm:max-w-[12rem] sm:pe-0"> <img loading="lazy"${addAttribute(item.cover.image.url, "src")} class="border-base-content bg-base-300 rounded-btn border border-opacity-5"${addAttribute(item.title, "alt")}> </figure>`} <div class="card-body"> <h2 class="text-xl">${item.title}</h2> <div class="mx-[-2px] mt-[-4px]"> <span class="badge badge-accent">${POST_TYPES[item.type]}</span> ${item.categories?.map((category) => renderTemplate`<span class="badge badge-primary">${category.name}</span>`)} ${item.tags?.map((tag) => renderTemplate`<span class="badge badge-secondary">${tag.name}</span>`)} </div> <div class="text-xs opacity-60 line-clamp-3"> ${item.description} </div> </div> </div> </a>`)} </div>`;
}, "/Users/littlesheep/Documents/Projects/Capital/src/components/PostList.astro", void 0);
const $$Astro$2 = createAstro("https://smartsheep.studio");
const prerender$2 = false;
const $$slug$2 = createComponent(async ($$result, $$props, $$slots) => {
const Astro2 = $$result.createAstro($$Astro$2, $$props, $$slots);
Astro2.self = $$slug$2;
const { slug } = Astro2.params;
const { posts } = (await graphQuery(
`query Query($where: PostWhereInput!, $orderBy: [PostOrderByInput!]!) {
posts(where: $where, orderBy: $orderBy) {
slug
type
title
description
cover {
image {
url
}
}
content {
document
}
categories {
name
}
tags {
name
}
createdAt
}
}`,
{
orderBy: [
{
createdAt: "desc"
}
],
where: { categories: { some: { slug: { equals: slug } } } }
}
)).data;
return renderTemplate`${renderComponent($$result, "PageLayout", $$PageLayout, { "title": "\u5206\u7C7B\u68C0\u7D22" }, { "default": ($$result2) => renderTemplate` ${maybeRenderHead()}<div class="max-w-[720px] mx-auto"> <div class="pt-16 pb-6 px-6"> <h1 class="text-4xl font-bold">分类检索</h1> <p class="pt-3"></p> </div> ${renderComponent($$result2, "PostList", $$PostList, { "posts": posts })} </div> ` })}`;
}, "/Users/littlesheep/Documents/Projects/Capital/src/pages/categories/[slug].astro", void 0);
const $$file$2 = "/Users/littlesheep/Documents/Projects/Capital/src/pages/categories/[slug].astro";
const $$url$2 = "/categories/[slug]";
const _slug_$2 = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
__proto__: null,
default: $$slug$2,
file: $$file$2,
prerender: prerender$2,
url: $$url$2
}, Symbol.toStringTag, { value: 'Module' }));
const $$Astro$1 = createAstro("https://smartsheep.studio");
const prerender$1 = false;
const $$slug$1 = createComponent(async ($$result, $$props, $$slots) => {
const Astro2 = $$result.createAstro($$Astro$1, $$props, $$slots);
Astro2.self = $$slug$1;
const { slug } = Astro2.params;
const { post } = (await graphQuery(
`query Query($where: PostWhereUniqueInput!) {
post(where: $where) {
slug
type
title
description
author {
name
}
assets {
caption
url
type
}
cover {
image {
url
}
}
content {
document
}
categories {
slug
name
}
tags {
slug
name
}
createdAt
}
}`,
{
where: { slug }
}
)).data;
return renderTemplate`${renderComponent($$result, "PageLayout", $$PageLayout, { "title": post.title, "data-astro-cid-gysqo7gh": true }, { "default": ($$result2) => renderTemplate` ${maybeRenderHead()}<div class="wrapper" data-astro-cid-gysqo7gh> <div class="card w-full shadow-xl" data-astro-cid-gysqo7gh> ${post.cover && renderTemplate`<figure data-astro-cid-gysqo7gh> <img${addAttribute(post.cover.image.url, "src")}${addAttribute(post.title, "alt")} data-astro-cid-gysqo7gh> </figure>`} <div class="card-body" data-astro-cid-gysqo7gh> <h2 class="card-title" data-astro-cid-gysqo7gh>${post.title}</h2> <p class="description" data-astro-cid-gysqo7gh>${post.description ?? "No description"}</p> <div class="divider" data-astro-cid-gysqo7gh></div> ${post.assets?.length > 0 && renderTemplate`<div class="mb-5 w-full" data-astro-cid-gysqo7gh> ${renderComponent($$result2, "Media", null, { "client:only": true, "sources": post.assets, "author": post.author, "client:component-hydration": "only", "data-astro-cid-gysqo7gh": true, "client:component-path": "/Users/littlesheep/Documents/Projects/Capital/src/components/posts/Media", "client:component-export": "default" })} </div>`} <div class="prose max-w-none" data-astro-cid-gysqo7gh> ${renderComponent($$result2, "DocumentRenderer", DocumentRenderer, { "document": post.content.document, "data-astro-cid-gysqo7gh": true })} </div> </div> </div> <div class="h-fit sticky top-header" data-astro-cid-gysqo7gh> <div class="card shadow-xl" data-astro-cid-gysqo7gh> <div class="card-body" data-astro-cid-gysqo7gh> <div class="gap-2 text-sm metadata description" data-astro-cid-gysqo7gh> <div data-astro-cid-gysqo7gh> <div data-astro-cid-gysqo7gh></div> <div data-astro-cid-gysqo7gh>${post.author?.name ?? "\u4F5A\u540D"}</div> </div> <div data-astro-cid-gysqo7gh> <div data-astro-cid-gysqo7gh></div> <div class="text-accent" data-astro-cid-gysqo7gh> ${POST_TYPES[post.type]} </div> </div> <div data-astro-cid-gysqo7gh> <div data-astro-cid-gysqo7gh></div> <div class="flex gap-1" data-astro-cid-gysqo7gh> ${post.categories?.map((category) => renderTemplate`<a${addAttribute(`/categories/${category.slug}`, "href")} class="link link-primary" data-astro-cid-gysqo7gh> ${category.name} </a>`)} </div> </div> <div data-astro-cid-gysqo7gh> <div data-astro-cid-gysqo7gh></div> <div class="flex gap-1" data-astro-cid-gysqo7gh> ${post.tags?.map((tag) => renderTemplate`<a${addAttribute(`/tags/${tag.slug}`, "href")} class="link link-secondary" data-astro-cid-gysqo7gh> ${tag.name} </a>`)} </div> </div> <div data-astro-cid-gysqo7gh> <div data-astro-cid-gysqo7gh></div> <div data-astro-cid-gysqo7gh>${new Date(post.createdAt).toLocaleString()}</div> </div> </div> </div> </div> </div> </div> ` })} `;
}, "/Users/littlesheep/Documents/Projects/Capital/src/pages/posts/[slug].astro", void 0);
const $$file$1 = "/Users/littlesheep/Documents/Projects/Capital/src/pages/posts/[slug].astro";
const $$url$1 = "/posts/[slug]";
const _slug_$1 = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
__proto__: null,
default: $$slug$1,
file: $$file$1,
prerender: prerender$1,
url: $$url$1
}, Symbol.toStringTag, { value: 'Module' }));
const $$Astro = createAstro("https://smartsheep.studio");
const prerender = false;
const $$slug = createComponent(async ($$result, $$props, $$slots) => {
const Astro2 = $$result.createAstro($$Astro, $$props, $$slots);
Astro2.self = $$slug;
const { slug } = Astro2.params;
const { posts } = (await graphQuery(
`query Query($where: PostWhereInput!, $orderBy: [PostOrderByInput!]!) {
posts(where: $where, orderBy: $orderBy) {
slug
type
title
description
cover {
image {
url
}
}
content {
document
}
categories {
name
}
tags {
name
}
createdAt
}
}`,
{
orderBy: [
{
createdAt: "desc"
}
],
where: { tags: { some: { slug: { equals: slug } } } }
}
)).data;
return renderTemplate`${renderComponent($$result, "PageLayout", $$PageLayout, { "title": "\u6807\u7B7E\u68C0\u7D22" }, { "default": ($$result2) => renderTemplate` ${maybeRenderHead()}<div class="max-w-[720px] mx-auto"> <div class="pt-16 pb-6 px-6"> <h1 class="text-4xl font-bold">标签检索</h1> <p class="pt-3"></p> </div> ${renderComponent($$result2, "PostList", $$PostList, { "posts": posts })} </div> ` })}`;
}, "/Users/littlesheep/Documents/Projects/Capital/src/pages/tags/[slug].astro", void 0);
const $$file = "/Users/littlesheep/Documents/Projects/Capital/src/pages/tags/[slug].astro";
const $$url = "/tags/[slug]";
const _slug_ = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
__proto__: null,
default: $$slug,
file: $$file,
prerender,
url: $$url
}, Symbol.toStringTag, { value: 'Module' }));
export { $$PageLayout as $, _slug_$2 as _, $$PostList as a, $$RootLayout as b, _slug_$1 as c, _slug_ as d, graphQuery as g };

View File

@ -0,0 +1,153 @@
/* empty css */
import { c as createAstro, d as createComponent, r as renderTemplate, h as renderComponent, m as maybeRenderHead, e as addAttribute } from '../astro_5WdVqH1c.mjs';
import 'kleur/colors';
import 'html-escaper';
import { g as graphQuery, $ as $$PageLayout, a as $$PostList, b as $$RootLayout } from './_slug__TUDhKBhQ.mjs';
import { DocumentRenderer } from '@keystone-6/document-renderer';
import 'clsx';
/* empty css */
const $$Astro$2 = createAstro("https://smartsheep.studio");
const prerender$2 = false;
const $$Index$2 = createComponent(async ($$result, $$props, $$slots) => {
const Astro2 = $$result.createAstro($$Astro$2, $$props, $$slots);
Astro2.self = $$Index$2;
const { events } = (await graphQuery(
`query Query($where: EventWhereInput!) {
events(where: $where) {
slug
title
description
content {
document
}
createdAt
}
}`,
{
where: {
isHistory: {
equals: true
}
}
}
)).data;
return renderTemplate`${renderComponent($$result, "PageLayout", $$PageLayout, { "title": "\u6D3B\u52A8" }, { "default": ($$result2) => renderTemplate` ${maybeRenderHead()}<div class="max-w-[720px] mx-auto"> <div class="card w-full shadow-xl"> <div class="card-body"> <h2 class="card-title">活动</h2> <p></p> <div class="divider"></div> <ul class="timeline timeline-snap-icon max-md:timeline-compact timeline-vertical"> ${events?.map((item, idx) => {
let align = idx % 2 === 0 ? "timeline-start" : "timeline-end";
let textAlign = idx % 2 === 0 ? "md:text-right" : "md:text-left";
return renderTemplate`<li> ${idx > 0 && renderTemplate`<hr>`} <div class="timeline-middle"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="h-5 w-5"> <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z" clip-rule="evenodd"></path> </svg> </div> <div${addAttribute(`${align} ${textAlign} mb-10`, "class")}> <time class="font-mono italic"> ${new Date(item.createdAt).toLocaleDateString()} </time> <div class="text-lg font-black">${item.title}</div> ${renderComponent($$result2, "DocumentRenderer", DocumentRenderer, { "document": item.content.document })} </div> <hr> </li>`;
})} </ul> <div class="text-center max-md:text-left italic">
我们的故事还在继续
</div> </div> </div> </div> ` })}`;
}, "/Users/littlesheep/Documents/Projects/Capital/src/pages/events/index.astro", void 0);
const $$file$2 = "/Users/littlesheep/Documents/Projects/Capital/src/pages/events/index.astro";
const $$url$2 = "/events";
const index$2 = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
__proto__: null,
default: $$Index$2,
file: $$file$2,
prerender: prerender$2,
url: $$url$2
}, Symbol.toStringTag, { value: 'Module' }));
const $$Astro$1 = createAstro("https://smartsheep.studio");
const prerender$1 = false;
const $$Index$1 = createComponent(async ($$result, $$props, $$slots) => {
const Astro2 = $$result.createAstro($$Astro$1, $$props, $$slots);
Astro2.self = $$Index$1;
const { posts } = (await graphQuery(
`query Query($where: PostWhereInput!, $orderBy: [PostOrderByInput!]!) {
posts(where: $where, orderBy: $orderBy) {
slug
type
title
description
cover {
image {
url
}
}
content {
document
}
categories {
name
}
tags {
name
}
createdAt
}
}`,
{
orderBy: [
{
createdAt: "desc"
}
],
where: {}
}
)).data;
return renderTemplate`${renderComponent($$result, "PageLayout", $$PageLayout, { "title": "\u8BB0\u5F55" }, { "default": ($$result2) => renderTemplate` ${maybeRenderHead()}<div class="max-w-[720px] mx-auto"> <div class="pt-16 pb-6 px-6"> <h1 class="text-4xl font-bold">记录</h1> <p class="pt-2"></p> </div> ${renderComponent($$result2, "PostList", $$PostList, { "posts": posts })} </div> ` })}`;
}, "/Users/littlesheep/Documents/Projects/Capital/src/pages/posts/index.astro", void 0);
const $$file$1 = "/Users/littlesheep/Documents/Projects/Capital/src/pages/posts/index.astro";
const $$url$1 = "/posts";
const index$1 = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
__proto__: null,
default: $$Index$1,
file: $$file$1,
prerender: prerender$1,
url: $$url$1
}, Symbol.toStringTag, { value: 'Module' }));
const $$Astro = createAstro("https://smartsheep.studio");
const prerender = false;
const $$Index = createComponent(async ($$result, $$props, $$slots) => {
const Astro2 = $$result.createAstro($$Astro, $$props, $$slots);
Astro2.self = $$Index;
const { events } = (await graphQuery(
`query Query($where: EventWhereInput!) {
events(where: $where) {
slug
title
description
createdAt
}
}`,
{
where: {
isHistory: {
equals: true
}
}
}
)).data;
return renderTemplate`${renderComponent($$result, "RootLayout", $$RootLayout, { "data-astro-cid-j7pv25f6": true }, { "default": ($$result2) => renderTemplate` ${maybeRenderHead()}<div class="max-h-fullpage mt-header wrapper px-5 snap-y snap-mandatory" data-astro-cid-j7pv25f6> <div id="hello" class="hero h-fullpage snap-start" data-astro-cid-j7pv25f6> <div class="hero-content w-full grid grid-cols-1 md:grid-cols-2 max-md:gap-[60px]" data-astro-cid-j7pv25f6> <div class="max-md:text-center" data-astro-cid-j7pv25f6> <h1 class="text-5xl font-bold" data-astro-cid-j7pv25f6>你好呀 👋</h1> <p class="py-6" data-astro-cid-j7pv25f6>
欢迎来到 SmartSheep Studio
的官方网站在这里了解订阅跟踪我们的最新消息
接触我们最大的官方社区并且尝试最新产品参与各种活动提供反馈让我们更好的服务您
</p> <a href="#about" class="btn btn-primary btn-md" data-astro-cid-j7pv25f6></a> </div> <div class="flex justify-center md:justify-end max-md:order-first" data-astro-cid-j7pv25f6> <div class="spinning p-3 md:p-5 shadow-2xl aspect-square rounded-[30%] w-[192px] md:w-[256px] lg:w-[384px]" data-astro-cid-j7pv25f6> <img src="/favicon.svg" alt="logo" loading="lazy" data-astro-cid-j7pv25f6> </div> </div> </div> </div> <div id="about" class="hero h-fullpage snap-start" data-astro-cid-j7pv25f6> <div class="hero-content w-full grid grid-cols-1 md:grid-cols-2 max-md:gap-[60px]" data-astro-cid-j7pv25f6> <div class="flex justify-center md:justify-start" data-astro-cid-j7pv25f6> <div class="stats shadow overflow-x-auto" data-astro-cid-j7pv25f6> <div class="stat" data-astro-cid-j7pv25f6> <div class="stat-figure text-secondary" data-astro-cid-j7pv25f6> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="inline-block w-8 h-8 stroke-current" data-astro-cid-j7pv25f6><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" data-astro-cid-j7pv25f6></path></svg> </div> <div class="stat-title" data-astro-cid-j7pv25f6>People</div> <div class="stat-value" data-astro-cid-j7pv25f6>1</div> <div class="stat-desc" data-astro-cid-j7pv25f6>2019 - ${(/* @__PURE__ */ new Date()).getFullYear()}</div> </div> <div class="stat" data-astro-cid-j7pv25f6> <div class="stat-figure text-secondary" data-astro-cid-j7pv25f6> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="inline-block w-8 h-8 stroke-current" data-astro-cid-j7pv25f6><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4" data-astro-cid-j7pv25f6></path></svg> </div> <div class="stat-title" data-astro-cid-j7pv25f6>Clients</div> <div class="stat-value" data-astro-cid-j7pv25f6>180</div> <div class="stat-desc" data-astro-cid-j7pv25f6> 80 (44%)</div> </div> <div class="stat" data-astro-cid-j7pv25f6> <div class="stat-figure text-secondary" data-astro-cid-j7pv25f6> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="inline-block w-8 h-8 stroke-current" data-astro-cid-j7pv25f6><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 8h14M5 8a2 2 0 110-4h14a2 2 0 110 4M5 8v10a2 2 0 002 2h10a2 2 0 002-2V8m-9 4h4" data-astro-cid-j7pv25f6></path></svg> </div> <div class="stat-title" data-astro-cid-j7pv25f6>Products</div> <div class="stat-value" data-astro-cid-j7pv25f6>4</div> <div class="stat-desc" data-astro-cid-j7pv25f6> 8 (67%)</div> </div> </div> </div> <div class="max-md:text-center" data-astro-cid-j7pv25f6> <h1 class="text-5xl font-bold" data-astro-cid-j7pv25f6> 🔖</h1> <p class="py-6" data-astro-cid-j7pv25f6>
我们是一群充满活力对开源充满热情的开发者成立于 2019
自那年起我们一直在开发让人喜欢的开源软件在我们这里取之于开源用之于开源
不仅是原则更是我们信仰的座右铭
</p> <a href="#history" class="btn btn-primary btn-md pl-[24px]" data-astro-cid-j7pv25f6>
查看岁月史书
</a> </div> </div> </div> <div id="history" class="flex flex-col justify-center items-center h-fullpage snap-start" data-astro-cid-j7pv25f6> <div class="text-center" data-astro-cid-j7pv25f6> <div data-astro-cid-j7pv25f6> <h1 class="text-4xl font-bold" data-astro-cid-j7pv25f6></h1> <p class="pt-2 pb-4 tracking-[8px]" data-astro-cid-j7pv25f6></p> <ul class="pb-6 mx-[-20px] max-w-[100vw] px-5 flex justify-center history timeline timeline-horizontal" data-astro-cid-j7pv25f6> ${events?.map((item, idx) => renderTemplate`<li data-astro-cid-j7pv25f6> ${idx > 0 && renderTemplate`<hr data-astro-cid-j7pv25f6>`} <div class="timeline-start" data-astro-cid-j7pv25f6> ${new Date(item.createdAt).toLocaleDateString()} </div> <div class="timeline-middle" data-astro-cid-j7pv25f6> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-5 h-5" data-astro-cid-j7pv25f6> <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z" clip-rule="evenodd" data-astro-cid-j7pv25f6></path> </svg> </div> <div class="timeline-end timeline-box" data-astro-cid-j7pv25f6> <h2 class="font-bold text-lg" data-astro-cid-j7pv25f6>${item.title}</h2> <div class="line-clamp-2" data-astro-cid-j7pv25f6>${item.description}</div> </div> ${idx < events?.length - 1 && renderTemplate`<hr data-astro-cid-j7pv25f6>`} </li>`)} </ul> <a class="btn btn-primary" href="/events" data-astro-cid-j7pv25f6></a> </div> </div> </div> </div> ` })} `;
}, "/Users/littlesheep/Documents/Projects/Capital/src/pages/index.astro", void 0);
const $$file = "/Users/littlesheep/Documents/Projects/Capital/src/pages/index.astro";
const $$url = "";
const index = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
__proto__: null,
default: $$Index,
file: $$file,
prerender,
url: $$url
}, Symbol.toStringTag, { value: 'Module' }));
export { index$1 as a, index as b, index$2 as i };

View File

@ -0,0 +1,240 @@
import { isRemotePath } from '@astrojs/internal-helpers/path';
import { readFile } from 'fs/promises';
import mime from 'mime/lite.js';
import 'os';
import { A as AstroError, j as InvalidImageService, k as ExpectedImageOptions, E as ExpectedImage, c as createAstro, d as createComponent, l as ImageMissingAlt, r as renderTemplate, m as maybeRenderHead, e as addAttribute, s as spreadAttributes } from '../astro_5WdVqH1c.mjs';
import { i as isESMImportedImage, a as isLocalService, b as isRemoteImage, D as DEFAULT_HASH_PROPS, c as isRemoteAllowed } from '../astro/assets-service_4dMyVCFm.mjs';
import 'html-escaper';
import 'clsx';
async function getConfiguredImageService() {
if (!globalThis?.astroAsset?.imageService) {
const { default: service } = await import(
// @ts-expect-error
'../astro/assets-service_4dMyVCFm.mjs'
).then(n => n.s).catch((e) => {
const error = new AstroError(InvalidImageService);
error.cause = e;
throw error;
});
if (!globalThis.astroAsset)
globalThis.astroAsset = {};
globalThis.astroAsset.imageService = service;
return service;
}
return globalThis.astroAsset.imageService;
}
async function getImage$1(options, imageConfig) {
if (!options || typeof options !== "object") {
throw new AstroError({
...ExpectedImageOptions,
message: ExpectedImageOptions.message(JSON.stringify(options))
});
}
if (typeof options.src === "undefined") {
throw new AstroError({
...ExpectedImage,
message: ExpectedImage.message(
options.src,
"undefined",
JSON.stringify(options)
)
});
}
const service = await getConfiguredImageService();
const resolvedOptions = {
...options,
src: typeof options.src === "object" && "then" in options.src ? (await options.src).default ?? await options.src : options.src
};
const originalPath = isESMImportedImage(resolvedOptions.src) ? resolvedOptions.src.fsPath : resolvedOptions.src;
const clonedSrc = isESMImportedImage(resolvedOptions.src) ? (
// @ts-expect-error - clone is a private, hidden prop
resolvedOptions.src.clone ?? resolvedOptions.src
) : resolvedOptions.src;
resolvedOptions.src = clonedSrc;
const validatedOptions = service.validateOptions ? await service.validateOptions(resolvedOptions, imageConfig) : resolvedOptions;
const srcSetTransforms = service.getSrcSet ? await service.getSrcSet(validatedOptions, imageConfig) : [];
let imageURL = await service.getURL(validatedOptions, imageConfig);
let srcSets = await Promise.all(
srcSetTransforms.map(async (srcSet) => ({
transform: srcSet.transform,
url: await service.getURL(srcSet.transform, imageConfig),
descriptor: srcSet.descriptor,
attributes: srcSet.attributes
}))
);
if (isLocalService(service) && globalThis.astroAsset.addStaticImage && !(isRemoteImage(validatedOptions.src) && imageURL === validatedOptions.src)) {
const propsToHash = service.propertiesToHash ?? DEFAULT_HASH_PROPS;
imageURL = globalThis.astroAsset.addStaticImage(validatedOptions, propsToHash, originalPath);
srcSets = srcSetTransforms.map((srcSet) => ({
transform: srcSet.transform,
url: globalThis.astroAsset.addStaticImage(srcSet.transform, propsToHash, originalPath),
descriptor: srcSet.descriptor,
attributes: srcSet.attributes
}));
}
return {
rawOptions: resolvedOptions,
options: validatedOptions,
src: imageURL,
srcSet: {
values: srcSets,
attribute: srcSets.map((srcSet) => `${srcSet.url} ${srcSet.descriptor}`).join(", ")
},
attributes: service.getHTMLAttributes !== void 0 ? await service.getHTMLAttributes(validatedOptions, imageConfig) : {}
};
}
const fnv1a52 = (str) => {
const len = str.length;
let i = 0, t0 = 0, v0 = 8997, t1 = 0, v1 = 33826, t2 = 0, v2 = 40164, t3 = 0, v3 = 52210;
while (i < len) {
v0 ^= str.charCodeAt(i++);
t0 = v0 * 435;
t1 = v1 * 435;
t2 = v2 * 435;
t3 = v3 * 435;
t2 += v0 << 8;
t3 += v1 << 8;
t1 += t0 >>> 16;
v0 = t0 & 65535;
t2 += t1 >>> 16;
v1 = t1 & 65535;
v3 = t3 + (t2 >>> 16) & 65535;
v2 = t2 & 65535;
}
return (v3 & 15) * 281474976710656 + v2 * 4294967296 + v1 * 65536 + (v0 ^ v3 >> 4);
};
const etag = (payload, weak = false) => {
const prefix = weak ? 'W/"' : '"';
return prefix + fnv1a52(payload).toString(36) + payload.length.toString(36) + '"';
};
const $$Astro$1 = createAstro("https://smartsheep.studio");
const $$Image = createComponent(async ($$result, $$props, $$slots) => {
const Astro2 = $$result.createAstro($$Astro$1, $$props, $$slots);
Astro2.self = $$Image;
const props = Astro2.props;
if (props.alt === void 0 || props.alt === null) {
throw new AstroError(ImageMissingAlt);
}
if (typeof props.width === "string") {
props.width = parseInt(props.width);
}
if (typeof props.height === "string") {
props.height = parseInt(props.height);
}
const image = await getImage(props);
const additionalAttributes = {};
if (image.srcSet.values.length > 0) {
additionalAttributes.srcset = image.srcSet.attribute;
}
return renderTemplate`${maybeRenderHead()}<img${addAttribute(image.src, "src")}${spreadAttributes(additionalAttributes)}${spreadAttributes(image.attributes)}>`;
}, "/Users/littlesheep/Documents/Projects/Capital/node_modules/astro/components/Image.astro", void 0);
const $$Astro = createAstro("https://smartsheep.studio");
const $$Picture = createComponent(async ($$result, $$props, $$slots) => {
const Astro2 = $$result.createAstro($$Astro, $$props, $$slots);
Astro2.self = $$Picture;
const defaultFormats = ["webp"];
const defaultFallbackFormat = "png";
const specialFormatsFallback = ["gif", "svg", "jpg", "jpeg"];
const { formats = defaultFormats, pictureAttributes = {}, fallbackFormat, ...props } = Astro2.props;
if (props.alt === void 0 || props.alt === null) {
throw new AstroError(ImageMissingAlt);
}
const optimizedImages = await Promise.all(
formats.map(
async (format) => await getImage({ ...props, format, widths: props.widths, densities: props.densities })
)
);
let resultFallbackFormat = fallbackFormat ?? defaultFallbackFormat;
if (!fallbackFormat && isESMImportedImage(props.src) && specialFormatsFallback.includes(props.src.format)) {
resultFallbackFormat = props.src.format;
}
const fallbackImage = await getImage({
...props,
format: resultFallbackFormat,
widths: props.widths,
densities: props.densities
});
const imgAdditionalAttributes = {};
const sourceAdditionaAttributes = {};
if (props.sizes) {
sourceAdditionaAttributes.sizes = props.sizes;
}
if (fallbackImage.srcSet.values.length > 0) {
imgAdditionalAttributes.srcset = fallbackImage.srcSet.attribute;
}
return renderTemplate`${maybeRenderHead()}<picture${spreadAttributes(pictureAttributes)}> ${Object.entries(optimizedImages).map(([_, image]) => {
const srcsetAttribute = props.densities || !props.densities && !props.widths ? `${image.src}${image.srcSet.values.length > 0 ? ", " + image.srcSet.attribute : ""}` : image.srcSet.attribute;
return renderTemplate`<source${addAttribute(srcsetAttribute, "srcset")}${addAttribute("image/" + image.options.format, "type")}${spreadAttributes(sourceAdditionaAttributes)}>`;
})} <img${addAttribute(fallbackImage.src, "src")}${spreadAttributes(imgAdditionalAttributes)}${spreadAttributes(fallbackImage.attributes)}> </picture>`;
}, "/Users/littlesheep/Documents/Projects/Capital/node_modules/astro/components/Picture.astro", void 0);
const imageConfig = {"service":{"entrypoint":"astro/assets/services/sharp","config":{}},"domains":[],"remotePatterns":[],"endpoint":"astro/assets/endpoint/node"};
const assetsDir = new URL("file:///Users/littlesheep/Documents/Projects/Capital/dist/client/");
const getImage = async (options) => await getImage$1(options, imageConfig);
async function loadLocalImage(src, url) {
const filePath = new URL("." + src, assetsDir);
let buffer = void 0;
try {
buffer = await readFile(filePath);
} catch (e) {
const sourceUrl = new URL(src, url.origin);
buffer = await loadRemoteImage(sourceUrl);
}
return buffer;
}
async function loadRemoteImage(src) {
try {
const res = await fetch(src);
if (!res.ok) {
return void 0;
}
return Buffer.from(await res.arrayBuffer());
} catch (err) {
return void 0;
}
}
const GET = async ({ request }) => {
try {
const imageService = await getConfiguredImageService();
if (!("transform" in imageService)) {
throw new Error("Configured image service is not a local service");
}
const url = new URL(request.url);
const transform = await imageService.parseURL(url, imageConfig);
if (!transform?.src) {
throw new Error("Incorrect transform returned by `parseURL`");
}
let inputBuffer = void 0;
if (isRemotePath(transform.src)) {
if (isRemoteAllowed(transform.src, imageConfig) === false) {
return new Response("Forbidden", { status: 403 });
}
inputBuffer = await loadRemoteImage(new URL(transform.src));
} else {
inputBuffer = await loadLocalImage(transform.src, url);
}
if (!inputBuffer) {
return new Response("Not Found", { status: 404 });
}
const { data, format } = await imageService.transform(inputBuffer, transform, imageConfig);
return new Response(data, {
status: 200,
headers: {
"Content-Type": mime.getType(format) ?? `image/${format}`,
"Cache-Control": "public, max-age=31536000",
ETag: etag(data.toString()),
Date: (/* @__PURE__ */ new Date()).toUTCString()
}
});
} catch (err) {
console.error("Could not process image request:", err);
return new Response(`Server Error: ${err}`, { status: 500 });
}
};
export { GET };

View File

@ -0,0 +1,31 @@
import { parse, DOCUMENT_NODE, ELEMENT_NODE, TEXT_NODE } from 'ultrahtml';
import { createElement, Fragment } from 'react';
let ids = 0;
function convert(children) {
let doc = parse(children.toString().trim());
let id = ids++;
let key = 0;
function createReactElementFromNode(node) {
const childVnodes =
Array.isArray(node.children) && node.children.length
? node.children.map((child) => createReactElementFromNode(child)).filter(Boolean)
: undefined;
if (node.type === DOCUMENT_NODE) {
return createElement(Fragment, {}, childVnodes);
} else if (node.type === ELEMENT_NODE) {
const { class: className, ...props } = node.attributes;
return createElement(node.name, { ...props, className, key: `${id}-${key++}` }, childVnodes);
} else if (node.type === TEXT_NODE) {
// 0-length text gets omitted in JSX
return node.value.trim() ? node.value : undefined;
}
}
const root = createReactElementFromNode(doc);
return root.props.children;
}
export { convert as default };

2355
test/data/warden/dist/server/entry.mjs vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,263 @@
import React, { createElement } from 'react';
import ReactDOM from 'react-dom/server';
/**
* Astro passes `children` as a string of HTML, so we need
* a wrapper `div` to render that content as VNodes.
*
* As a bonus, we can signal to React that this subtree is
* entirely static and will never change via `shouldComponentUpdate`.
*/
const StaticHtml = ({ value, name, hydrate = true }) => {
if (!value) return null;
const tagName = hydrate ? 'astro-slot' : 'astro-static-slot';
return createElement(tagName, {
name,
suppressHydrationWarning: true,
dangerouslySetInnerHTML: { __html: value },
});
};
/**
* This tells React to opt-out of re-rendering this subtree,
* In addition to being a performance optimization,
* this also allows other frameworks to attach to `children`.
*
* See https://preactjs.com/guide/v8/external-dom-mutations
*/
StaticHtml.shouldComponentUpdate = () => false;
const contexts = new WeakMap();
const ID_PREFIX = 'r';
function getContext(rendererContextResult) {
if (contexts.has(rendererContextResult)) {
return contexts.get(rendererContextResult);
}
const ctx = {
currentIndex: 0,
get id() {
return ID_PREFIX + this.currentIndex.toString();
},
};
contexts.set(rendererContextResult, ctx);
return ctx;
}
function incrementId(rendererContextResult) {
const ctx = getContext(rendererContextResult);
const id = ctx.id;
ctx.currentIndex++;
return id;
}
const opts = {
experimentalReactChildren: false
};
const slotName = (str) => str.trim().replace(/[-_]([a-z])/g, (_, w) => w.toUpperCase());
const reactTypeof = Symbol.for('react.element');
function errorIsComingFromPreactComponent(err) {
return (
err.message &&
(err.message.startsWith("Cannot read property '__H'") ||
err.message.includes("(reading '__H')"))
);
}
async function check(Component, props, children) {
// Note: there are packages that do some unholy things to create "components".
// Checking the $$typeof property catches most of these patterns.
if (typeof Component === 'object') {
return Component['$$typeof'].toString().slice('Symbol('.length).startsWith('react');
}
if (typeof Component !== 'function') return false;
if (Component.name === 'QwikComponent') return false;
// Preact forwarded-ref components can be functions, which React does not support
if (typeof Component === 'function' && Component['$$typeof'] === Symbol.for('react.forward_ref'))
return false;
if (Component.prototype != null && typeof Component.prototype.render === 'function') {
return React.Component.isPrototypeOf(Component) || React.PureComponent.isPrototypeOf(Component);
}
let error = null;
let isReactComponent = false;
function Tester(...args) {
try {
const vnode = Component(...args);
if (vnode && vnode['$$typeof'] === reactTypeof) {
isReactComponent = true;
}
} catch (err) {
if (!errorIsComingFromPreactComponent(err)) {
error = err;
}
}
return React.createElement('div');
}
await renderToStaticMarkup(Tester, props, children, {});
if (error) {
throw error;
}
return isReactComponent;
}
async function getNodeWritable() {
let nodeStreamBuiltinModuleName = 'node:stream';
let { Writable } = await import(/* @vite-ignore */ nodeStreamBuiltinModuleName);
return Writable;
}
function needsHydration(metadata) {
// Adjust how this is hydrated only when the version of Astro supports `astroStaticSlot`
return metadata.astroStaticSlot ? !!metadata.hydrate : true;
}
async function renderToStaticMarkup(Component, props, { default: children, ...slotted }, metadata) {
let prefix;
if (this && this.result) {
prefix = incrementId(this.result);
}
const attrs = { prefix };
delete props['class'];
const slots = {};
for (const [key, value] of Object.entries(slotted)) {
const name = slotName(key);
slots[name] = React.createElement(StaticHtml, {
hydrate: needsHydration(metadata),
value,
name,
});
}
// Note: create newProps to avoid mutating `props` before they are serialized
const newProps = {
...props,
...slots,
};
const newChildren = children ?? props.children;
if (children && opts.experimentalReactChildren) {
attrs['data-react-children'] = true;
const convert = await import('./chunks/vnode-children_3wEZly-Z.mjs').then((mod) => mod.default);
newProps.children = convert(children);
} else if (newChildren != null) {
newProps.children = React.createElement(StaticHtml, {
hydrate: needsHydration(metadata),
value: newChildren,
});
}
const vnode = React.createElement(Component, newProps);
const renderOptions = {
identifierPrefix: prefix,
};
let html;
if (metadata?.hydrate) {
if ('renderToReadableStream' in ReactDOM) {
html = await renderToReadableStreamAsync(vnode, renderOptions);
} else {
html = await renderToPipeableStreamAsync(vnode, renderOptions);
}
} else {
if ('renderToReadableStream' in ReactDOM) {
html = await renderToReadableStreamAsync(vnode, renderOptions);
} else {
html = await renderToStaticNodeStreamAsync(vnode, renderOptions);
}
}
return { html, attrs };
}
async function renderToPipeableStreamAsync(vnode, options) {
const Writable = await getNodeWritable();
let html = '';
return new Promise((resolve, reject) => {
let error = undefined;
let stream = ReactDOM.renderToPipeableStream(vnode, {
...options,
onError(err) {
error = err;
reject(error);
},
onAllReady() {
stream.pipe(
new Writable({
write(chunk, _encoding, callback) {
html += chunk.toString('utf-8');
callback();
},
destroy() {
resolve(html);
},
})
);
},
});
});
}
async function renderToStaticNodeStreamAsync(vnode, options) {
const Writable = await getNodeWritable();
let html = '';
return new Promise((resolve, reject) => {
let stream = ReactDOM.renderToStaticNodeStream(vnode, options);
stream.on('error', (err) => {
reject(err);
});
stream.pipe(
new Writable({
write(chunk, _encoding, callback) {
html += chunk.toString('utf-8');
callback();
},
destroy() {
resolve(html);
},
})
);
});
}
/**
* Use a while loop instead of "for await" due to cloudflare and Vercel Edge issues
* See https://github.com/facebook/react/issues/24169
*/
async function readResult(stream) {
const reader = stream.getReader();
let result = '';
const decoder = new TextDecoder('utf-8');
while (true) {
const { done, value } = await reader.read();
if (done) {
if (value) {
result += decoder.decode(value);
} else {
// This closes the decoder
decoder.decode(new Uint8Array());
}
return result;
}
result += decoder.decode(value, { stream: true });
}
}
async function renderToReadableStreamAsync(vnode, options) {
return await readResult(await ReactDOM.renderToReadableStream(vnode, options));
}
const _renderer0 = {
check,
renderToStaticMarkup,
supportsAstroStaticSlot: true,
};
const renderers = [Object.assign({"name":"@astrojs/react","clientEntrypoint":"@astrojs/react/client.js","serverEntrypoint":"@astrojs/react/server.js"}, { ssr: _renderer0 }),];
export { renderers };