This commit is contained in:
parent
8c67595ca9
commit
ceaa43f87d
36
.dockerignore
Normal file
36
.dockerignore
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
# Git相关
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
|
||||||
|
# Rust构建产物
|
||||||
|
target/
|
||||||
|
Cargo.lock
|
||||||
|
|
||||||
|
# IDE相关
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
|
||||||
|
# 系统文件
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# 日志文件
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# 临时文件
|
||||||
|
*.tmp
|
||||||
|
*.temp
|
||||||
|
|
||||||
|
# 文档
|
||||||
|
README.md
|
||||||
|
docs/
|
||||||
|
|
||||||
|
# 测试文件
|
||||||
|
tests/
|
||||||
|
**/*_test.rs
|
||||||
|
|
||||||
|
# 其他
|
||||||
|
.env.local
|
||||||
|
.env.production
|
||||||
61
.github/workflows/docker.yml
vendored
Normal file
61
.github/workflows/docker.yml
vendored
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
name: Docker Build and Push
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ main, master ]
|
||||||
|
tags: [ 'v*' ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ main, master ]
|
||||||
|
|
||||||
|
env:
|
||||||
|
REGISTRY: ghcr.io
|
||||||
|
IMAGE_NAME: ${{ github.repository }}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Log in to Container Registry
|
||||||
|
if: github.event_name != 'pull_request'
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ${{ env.REGISTRY }}
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Extract metadata
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||||
|
tags: |
|
||||||
|
type=ref,event=branch
|
||||||
|
type=ref,event=pr
|
||||||
|
type=semver,pattern={{version}}
|
||||||
|
type=semver,pattern={{major}}.{{minor}}
|
||||||
|
type=sha,prefix={{branch}}-
|
||||||
|
|
||||||
|
- name: Build and push Docker image
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
push: ${{ github.event_name != 'pull_request' }}
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
cache-from: type=gha
|
||||||
|
cache-to: type=gha,mode=max
|
||||||
|
|
||||||
|
- name: Output image digest
|
||||||
|
if: github.event_name != 'pull_request'
|
||||||
|
run: echo ${{ steps.build.outputs.digest }}
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
@ -1 +1,3 @@
|
|||||||
/target
|
/target
|
||||||
|
|
||||||
|
.env
|
||||||
310
Cargo.lock
generated
310
Cargo.lock
generated
@ -56,6 +56,12 @@ dependencies = [
|
|||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atomic-waker"
|
||||||
|
version = "1.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
@ -204,6 +210,7 @@ dependencies = [
|
|||||||
"iana-time-zone",
|
"iana-time-zone",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
|
"serde",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"windows-link",
|
"windows-link",
|
||||||
]
|
]
|
||||||
@ -337,6 +344,15 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "encoding_rs"
|
||||||
|
version = "0.8.35"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "equivalent"
|
name = "equivalent"
|
||||||
version = "1.0.2"
|
version = "1.0.2"
|
||||||
@ -539,6 +555,25 @@ version = "0.31.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "h2"
|
||||||
|
version = "0.4.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "17da50a276f1e01e0ba6c029e47b7100754904ee8a278f886546e98575380785"
|
||||||
|
dependencies = [
|
||||||
|
"atomic-waker",
|
||||||
|
"bytes",
|
||||||
|
"fnv",
|
||||||
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
|
"http",
|
||||||
|
"indexmap",
|
||||||
|
"slab",
|
||||||
|
"tokio",
|
||||||
|
"tokio-util",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.15.4"
|
version = "0.15.4"
|
||||||
@ -653,6 +688,7 @@ dependencies = [
|
|||||||
"bytes",
|
"bytes",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
|
"h2",
|
||||||
"http",
|
"http",
|
||||||
"http-body",
|
"http-body",
|
||||||
"httparse",
|
"httparse",
|
||||||
@ -661,6 +697,39 @@ dependencies = [
|
|||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"want",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hyper-rustls"
|
||||||
|
version = "0.27.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58"
|
||||||
|
dependencies = [
|
||||||
|
"http",
|
||||||
|
"hyper",
|
||||||
|
"hyper-util",
|
||||||
|
"rustls",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"tokio",
|
||||||
|
"tokio-rustls",
|
||||||
|
"tower-service",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hyper-tls"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"http-body-util",
|
||||||
|
"hyper",
|
||||||
|
"hyper-util",
|
||||||
|
"native-tls",
|
||||||
|
"tokio",
|
||||||
|
"tokio-native-tls",
|
||||||
|
"tower-service",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -669,14 +738,24 @@ version = "0.1.16"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e"
|
checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"base64",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
"futures-util",
|
||||||
"http",
|
"http",
|
||||||
"http-body",
|
"http-body",
|
||||||
"hyper",
|
"hyper",
|
||||||
|
"ipnet",
|
||||||
|
"libc",
|
||||||
|
"percent-encoding",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
|
"socket2",
|
||||||
|
"system-configuration",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
|
"tracing",
|
||||||
|
"windows-registry",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -831,6 +910,22 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ipnet"
|
||||||
|
version = "2.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iri-string"
|
||||||
|
version = "0.7.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.15"
|
version = "1.0.15"
|
||||||
@ -925,11 +1020,13 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"lru",
|
"lru",
|
||||||
"regex",
|
"regex",
|
||||||
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tower-http",
|
"tower-http",
|
||||||
|
"url",
|
||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1297,6 +1394,60 @@ version = "0.8.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "reqwest"
|
||||||
|
version = "0.12.22"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531"
|
||||||
|
dependencies = [
|
||||||
|
"base64",
|
||||||
|
"bytes",
|
||||||
|
"encoding_rs",
|
||||||
|
"futures-core",
|
||||||
|
"h2",
|
||||||
|
"http",
|
||||||
|
"http-body",
|
||||||
|
"http-body-util",
|
||||||
|
"hyper",
|
||||||
|
"hyper-rustls",
|
||||||
|
"hyper-tls",
|
||||||
|
"hyper-util",
|
||||||
|
"js-sys",
|
||||||
|
"log",
|
||||||
|
"mime",
|
||||||
|
"native-tls",
|
||||||
|
"percent-encoding",
|
||||||
|
"pin-project-lite",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"serde_urlencoded",
|
||||||
|
"sync_wrapper",
|
||||||
|
"tokio",
|
||||||
|
"tokio-native-tls",
|
||||||
|
"tower",
|
||||||
|
"tower-http",
|
||||||
|
"tower-service",
|
||||||
|
"url",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"wasm-bindgen-futures",
|
||||||
|
"web-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ring"
|
||||||
|
version = "0.17.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"cfg-if",
|
||||||
|
"getrandom 0.2.16",
|
||||||
|
"libc",
|
||||||
|
"untrusted",
|
||||||
|
"windows-sys 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rsa"
|
name = "rsa"
|
||||||
version = "0.9.8"
|
version = "0.9.8"
|
||||||
@ -1336,6 +1487,39 @@ dependencies = [
|
|||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls"
|
||||||
|
version = "0.23.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"rustls-webpki",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-pki-types"
|
||||||
|
version = "1.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79"
|
||||||
|
dependencies = [
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-webpki"
|
||||||
|
version = "0.103.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc"
|
||||||
|
dependencies = [
|
||||||
|
"ring",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"untrusted",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustversion"
|
name = "rustversion"
|
||||||
version = "1.0.21"
|
version = "1.0.21"
|
||||||
@ -1767,6 +1951,9 @@ name = "sync_wrapper"
|
|||||||
version = "1.0.2"
|
version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
|
checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "synstructure"
|
name = "synstructure"
|
||||||
@ -1779,6 +1966,27 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "system-configuration"
|
||||||
|
version = "0.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"core-foundation",
|
||||||
|
"system-configuration-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "system-configuration-sys"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4"
|
||||||
|
dependencies = [
|
||||||
|
"core-foundation-sys",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tempfile"
|
name = "tempfile"
|
||||||
version = "3.20.0"
|
version = "3.20.0"
|
||||||
@ -1868,6 +2076,26 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-native-tls"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
|
||||||
|
dependencies = [
|
||||||
|
"native-tls",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-rustls"
|
||||||
|
version = "0.26.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b"
|
||||||
|
dependencies = [
|
||||||
|
"rustls",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-stream"
|
name = "tokio-stream"
|
||||||
version = "0.1.17"
|
version = "0.1.17"
|
||||||
@ -1879,6 +2107,19 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-util"
|
||||||
|
version = "0.7.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
|
"pin-project-lite",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tower"
|
name = "tower"
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
@ -1903,8 +2144,12 @@ checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
"futures-util",
|
||||||
"http",
|
"http",
|
||||||
|
"http-body",
|
||||||
|
"iri-string",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
|
"tower",
|
||||||
"tower-layer",
|
"tower-layer",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
]
|
]
|
||||||
@ -1953,6 +2198,12 @@ dependencies = [
|
|||||||
"once_cell",
|
"once_cell",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "try-lock"
|
||||||
|
version = "0.2.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typenum"
|
name = "typenum"
|
||||||
version = "1.18.0"
|
version = "1.18.0"
|
||||||
@ -1986,6 +2237,12 @@ version = "0.1.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0"
|
checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "untrusted"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "url"
|
name = "url"
|
||||||
version = "2.5.4"
|
version = "2.5.4"
|
||||||
@ -2011,6 +2268,7 @@ checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom 0.3.3",
|
"getrandom 0.3.3",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
|
"serde",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -2026,6 +2284,15 @@ version = "0.9.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "want"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
|
||||||
|
dependencies = [
|
||||||
|
"try-lock",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.11.1+wasi-snapshot-preview1"
|
version = "0.11.1+wasi-snapshot-preview1"
|
||||||
@ -2073,6 +2340,19 @@ dependencies = [
|
|||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-futures"
|
||||||
|
version = "0.4.50"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"js-sys",
|
||||||
|
"once_cell",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"web-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-macro"
|
name = "wasm-bindgen-macro"
|
||||||
version = "0.2.100"
|
version = "0.2.100"
|
||||||
@ -2105,6 +2385,16 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "web-sys"
|
||||||
|
version = "0.3.77"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2"
|
||||||
|
dependencies = [
|
||||||
|
"js-sys",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "whoami"
|
name = "whoami"
|
||||||
version = "1.6.0"
|
version = "1.6.0"
|
||||||
@ -2156,6 +2446,17 @@ version = "0.1.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
|
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-registry"
|
||||||
|
version = "0.5.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
"windows-result",
|
||||||
|
"windows-strings",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-result"
|
name = "windows-result"
|
||||||
version = "0.3.4"
|
version = "0.3.4"
|
||||||
@ -2183,6 +2484,15 @@ dependencies = [
|
|||||||
"windows-targets 0.48.5",
|
"windows-targets 0.48.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets 0.52.6",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.59.0"
|
version = "0.59.0"
|
||||||
|
|||||||
@ -5,14 +5,16 @@ edition = "2024"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
axum = "0.8.4"
|
axum = "0.8.4"
|
||||||
chrono = "0.4.41"
|
chrono = { version = "0.4.41", features = ["serde"] }
|
||||||
dotenvy = "0.15.7"
|
dotenvy = "0.15.7"
|
||||||
log = "0.4.27"
|
log = "0.4.27"
|
||||||
lru = "0.16.0"
|
lru = "0.16.0"
|
||||||
regex = "1.11.1"
|
regex = "1.11.1"
|
||||||
|
reqwest = { version = "0.12", features = ["json"] }
|
||||||
serde = { version = "1.0.219", features = ["derive"] }
|
serde = { version = "1.0.219", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
sqlx = { version = "0.8.6", features = ["runtime-tokio-native-tls", "postgres", "uuid", "chrono"] }
|
sqlx = { version = "0.8.6", features = ["runtime-tokio-native-tls", "postgres", "uuid", "chrono"] }
|
||||||
tokio = { version = "1.47.0", features = ["full"] }
|
tokio = { version = "1.47.0", features = ["full"] }
|
||||||
tower-http = {version = "0.6.6" , features = ["cors"]}
|
tower-http = {version = "0.6.6" , features = ["cors"]}
|
||||||
uuid = { version = "1.17.0", features = ["v4"] }
|
url = "2.5.4"
|
||||||
|
uuid = { version = "1.17.0", features = ["v4", "serde"] }
|
||||||
|
|||||||
60
Dockerfile
Normal file
60
Dockerfile
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
# 使用官方Rust镜像作为构建阶段
|
||||||
|
FROM rust:1.75-slim as builder
|
||||||
|
|
||||||
|
# 设置工作目录
|
||||||
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
|
# 复制Cargo.toml和Cargo.lock文件
|
||||||
|
COPY Cargo.toml Cargo.lock ./
|
||||||
|
|
||||||
|
# 创建一个虚拟的main.rs来缓存依赖
|
||||||
|
RUN mkdir src && echo "fn main() {}" > src/main.rs
|
||||||
|
|
||||||
|
# 构建依赖(这会缓存依赖层)
|
||||||
|
RUN cargo build --release
|
||||||
|
|
||||||
|
# 删除虚拟的main.rs
|
||||||
|
RUN rm src/main.rs
|
||||||
|
|
||||||
|
# 复制源代码
|
||||||
|
COPY src ./src
|
||||||
|
|
||||||
|
# 重新构建应用程序(现在会使用缓存的依赖)
|
||||||
|
RUN cargo build --release
|
||||||
|
|
||||||
|
# 运行时阶段
|
||||||
|
FROM debian:bookworm-slim
|
||||||
|
|
||||||
|
# 安装运行时依赖
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
ca-certificates \
|
||||||
|
libpq-dev \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# 创建非root用户
|
||||||
|
RUN groupadd -r appuser && useradd -r -g appuser appuser
|
||||||
|
|
||||||
|
# 设置工作目录
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# 从构建阶段复制二进制文件
|
||||||
|
COPY --from=builder /usr/src/app/target/release/mapp-tile /app/mapp-tile
|
||||||
|
|
||||||
|
# 复制配置文件(如果有的话)
|
||||||
|
COPY .env* ./
|
||||||
|
|
||||||
|
# 更改文件所有权
|
||||||
|
RUN chown -R appuser:appuser /app
|
||||||
|
|
||||||
|
# 切换到非root用户
|
||||||
|
USER appuser
|
||||||
|
|
||||||
|
# 暴露端口(根据您的应用程序需要调整)
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
# 设置健康检查
|
||||||
|
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||||
|
CMD curl -f http://localhost:3000/api/v1/health || exit 1
|
||||||
|
|
||||||
|
# 运行应用程序
|
||||||
|
CMD ["./mapp-tile"]
|
||||||
100
README.md
Normal file
100
README.md
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
# Mapp Tile Service
|
||||||
|
|
||||||
|
一个基于Rust的瓦片地图服务,提供图像缓存和数据库查询功能。
|
||||||
|
|
||||||
|
## 🐳 Docker 部署
|
||||||
|
|
||||||
|
### 本地构建
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 构建镜像
|
||||||
|
docker build -t mapp-tile:latest .
|
||||||
|
|
||||||
|
# 运行容器
|
||||||
|
docker run -p 3000:3000 --env-file .env mapp-tile:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
### 使用构建脚本
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 给脚本执行权限
|
||||||
|
chmod +x docker-build.sh
|
||||||
|
|
||||||
|
# 运行构建脚本
|
||||||
|
./docker-build.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🚀 GitHub Actions
|
||||||
|
|
||||||
|
本项目配置了以下GitHub Actions工作流:
|
||||||
|
|
||||||
|
### 1. Docker构建和推送 (`docker.yml`)
|
||||||
|
- 在推送到main/master分支时自动构建Docker镜像
|
||||||
|
- 支持多平台构建(amd64, arm64)
|
||||||
|
- 自动推送到GitHub Container Registry
|
||||||
|
- 支持语义化版本标签
|
||||||
|
|
||||||
|
### 2. 测试 (`test.yml`)
|
||||||
|
- 运行Rust项目的单元测试
|
||||||
|
- 代码格式检查
|
||||||
|
- Clippy静态分析
|
||||||
|
- 包含PostgreSQL服务用于集成测试
|
||||||
|
|
||||||
|
### 3. 安全扫描 (`security.yml`)
|
||||||
|
- 依赖漏洞扫描(cargo audit)
|
||||||
|
- 许可证检查(cargo-deny)
|
||||||
|
- 容器镜像安全扫描(Trivy)
|
||||||
|
- 每周自动运行
|
||||||
|
|
||||||
|
## 📋 环境变量
|
||||||
|
|
||||||
|
创建 `.env` 文件并配置以下环境变量:
|
||||||
|
|
||||||
|
```env
|
||||||
|
DATABASE_URL=postgresql://username:password@localhost:5432/database
|
||||||
|
LOCAL_OSS=https://your-oss-endpoint.com
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔧 开发
|
||||||
|
|
||||||
|
### 本地运行
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 安装依赖
|
||||||
|
cargo build
|
||||||
|
|
||||||
|
# 运行开发服务器
|
||||||
|
cargo run
|
||||||
|
```
|
||||||
|
|
||||||
|
### 测试
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 运行测试
|
||||||
|
cargo test
|
||||||
|
|
||||||
|
# 运行Clippy检查
|
||||||
|
cargo clippy
|
||||||
|
|
||||||
|
# 检查代码格式
|
||||||
|
cargo fmt --check
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📦 镜像标签说明
|
||||||
|
|
||||||
|
- `latest`: 最新版本
|
||||||
|
- `main`: main分支的最新构建
|
||||||
|
- `v1.0.0`: 语义化版本标签
|
||||||
|
- `sha-abc123`: 基于提交哈希的标签
|
||||||
|
|
||||||
|
## 🔍 健康检查
|
||||||
|
|
||||||
|
服务提供健康检查端点:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl http://localhost:3000/api/v1/health
|
||||||
|
```
|
||||||
|
|
||||||
|
## <20><> 许可证
|
||||||
|
|
||||||
|
MIT License
|
||||||
143
src/app.rs
143
src/app.rs
@ -1,35 +1,48 @@
|
|||||||
use axum::{Error, response::Response};
|
use axum::{Error, response::Response};
|
||||||
use std::num::NonZero;
|
use std::num::NonZero;
|
||||||
|
|
||||||
use crate::{config::Config, model::inputs::TileInfo, service::Service};
|
use crate::{
|
||||||
|
config::Config,
|
||||||
|
model::{inputs::TileInfo, responses::TileResponse},
|
||||||
|
service::Service,
|
||||||
|
utils::build_url,
|
||||||
|
};
|
||||||
use axum::{
|
use axum::{
|
||||||
Router, ServiceExt,
|
Router, ServiceExt,
|
||||||
extract::{FromRef, Query, State},
|
extract::{FromRef, Query, State},
|
||||||
http::StatusCode,
|
http::{HeaderMap, StatusCode, header},
|
||||||
response::{Html, IntoResponse},
|
response::{Html, IntoResponse, Redirect},
|
||||||
routing::{get, post},
|
routing::{get, post},
|
||||||
};
|
};
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use lru::LruCache;
|
use lru::LruCache;
|
||||||
|
use reqwest;
|
||||||
|
use serde_json;
|
||||||
use sqlx::postgres::PgPool;
|
use sqlx::postgres::PgPool;
|
||||||
|
use std::collections::HashMap;
|
||||||
use tower_http::cors::CorsLayer;
|
use tower_http::cors::CorsLayer;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct AppState {
|
pub struct AppState {
|
||||||
pub service: Service,
|
pub service: Service,
|
||||||
pub pool: PgPool,
|
pub pool: PgPool,
|
||||||
pub cache: LruCache<DateTime<Utc>, Vec<u8>>,
|
pub cache: LruCache<DateTime<Utc>, TileResponse>,
|
||||||
|
pub image_cache: LruCache<String, (Vec<u8>, String)>, // URL -> (image_data, content_type)
|
||||||
|
pub config: Config,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create_router(config: &Config) -> Router {
|
pub async fn create_router(config: &Config) -> Router {
|
||||||
let pool = PgPool::connect(&config.database_url).await.unwrap();
|
let pool = PgPool::connect(&config.database_url).await.unwrap();
|
||||||
let cache = LruCache::new(NonZero::new(10).unwrap());
|
let cache = LruCache::new(NonZero::new(10).unwrap());
|
||||||
|
let image_cache = LruCache::new(NonZero::new(20).unwrap()); // 缓存更多图像
|
||||||
|
|
||||||
let service = Service::new(pool.clone());
|
let service = Service::new(pool.clone());
|
||||||
let app_state = AppState {
|
let app_state = AppState {
|
||||||
service,
|
service,
|
||||||
pool,
|
pool,
|
||||||
cache,
|
cache,
|
||||||
|
image_cache,
|
||||||
|
config: config.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
Router::new()
|
Router::new()
|
||||||
@ -50,33 +63,129 @@ async fn data_handler(
|
|||||||
match chrono::NaiveDateTime::parse_from_str(¶ms.datetime, "%Y%m%d%H%M%S") {
|
match chrono::NaiveDateTime::parse_from_str(¶ms.datetime, "%Y%m%d%H%M%S") {
|
||||||
Ok(naive_datetime) => {
|
Ok(naive_datetime) => {
|
||||||
let datetime = naive_datetime.and_utc();
|
let datetime = naive_datetime.and_utc();
|
||||||
if let Some(tile_info) = state.cache.get(&datetime) {
|
let area = params.area.unwrap_or_default();
|
||||||
return Html(format!("tile_info: {:?}", tile_info)).into_response();
|
|
||||||
|
// 先检查缓存
|
||||||
|
if let Some(tile_response) = state.cache.get(&datetime) {
|
||||||
|
// 检查图像缓存
|
||||||
|
if let Some((image_data, content_type)) =
|
||||||
|
state.image_cache.get(&tile_response.storage_url)
|
||||||
|
{
|
||||||
|
let mut headers = HeaderMap::new();
|
||||||
|
headers.insert(header::CONTENT_TYPE, content_type.parse().unwrap());
|
||||||
|
headers.insert(header::CONTENT_DISPOSITION, "inline".parse().unwrap());
|
||||||
|
return (StatusCode::OK, headers, image_data.clone()).into_response();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下载图像数据并返回
|
||||||
|
match download_image(&tile_response.storage_url).await {
|
||||||
|
Ok((image_data, content_type)) => {
|
||||||
|
// 缓存图像数据
|
||||||
|
state.image_cache.put(
|
||||||
|
tile_response.storage_url.clone(),
|
||||||
|
(image_data.clone(), content_type.clone()),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut headers = HeaderMap::new();
|
||||||
|
headers.insert(header::CONTENT_TYPE, content_type.parse().unwrap());
|
||||||
|
headers.insert(header::CONTENT_DISPOSITION, "inline".parse().unwrap());
|
||||||
|
(StatusCode::OK, headers, image_data).into_response()
|
||||||
|
}
|
||||||
|
Err(e) => (
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
format!("下载图像失败: {:?}", e),
|
||||||
|
)
|
||||||
|
.into_response(),
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
match state.service.get_tile_data(datetime, "").await {
|
// 从数据库获取
|
||||||
Ok(tile_info) => {
|
match state.service.get_tile_data(datetime, &area).await {
|
||||||
// state.cache.put(datetime, tile_info.clone());
|
Ok(tile_response) => {
|
||||||
(StatusCode::OK, Html(format!("tile_info: {:?}", tile_info)))
|
// 缓存结果
|
||||||
.into_response()
|
state.cache.put(datetime, tile_response.clone());
|
||||||
|
|
||||||
|
// 检查图像缓存
|
||||||
|
if let Some((image_data, content_type)) =
|
||||||
|
state.image_cache.get(&tile_response.storage_url)
|
||||||
|
{
|
||||||
|
let mut headers = HeaderMap::new();
|
||||||
|
headers.insert(header::CONTENT_TYPE, content_type.parse().unwrap());
|
||||||
|
headers.insert(header::CONTENT_DISPOSITION, "inline".parse().unwrap());
|
||||||
|
return (StatusCode::OK, headers, image_data.clone()).into_response();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下载图像数据并返回
|
||||||
|
let url = build_url(&state.config.local_oss, &[&tile_response.storage_url])
|
||||||
|
.map_err(|e| {
|
||||||
|
(
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
format!("构建URL失败: {:?}", e),
|
||||||
|
)
|
||||||
|
.into_response()
|
||||||
|
})
|
||||||
|
.unwrap()
|
||||||
|
.replace(".png", "_processed.png");
|
||||||
|
|
||||||
|
match download_image(&url).await {
|
||||||
|
Ok((image_data, content_type)) => {
|
||||||
|
// 缓存图像数据
|
||||||
|
state.image_cache.put(
|
||||||
|
tile_response.storage_url.clone(),
|
||||||
|
(image_data.clone(), content_type.clone()),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut headers = HeaderMap::new();
|
||||||
|
headers.insert(header::CONTENT_TYPE, content_type.parse().unwrap());
|
||||||
|
headers
|
||||||
|
.insert(header::CONTENT_DISPOSITION, "inline".parse().unwrap());
|
||||||
|
(StatusCode::OK, headers, image_data).into_response()
|
||||||
|
}
|
||||||
|
Err(e) => (
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
format!("下载图像失败: {:?}", e),
|
||||||
|
)
|
||||||
|
.into_response(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
// 方式1: 返回错误状态码和错误信息
|
// 返回错误状态码和错误信息
|
||||||
return (
|
(
|
||||||
StatusCode::INTERNAL_SERVER_ERROR,
|
StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
format!("数据库错误: {:?}", e),
|
format!("数据库错误: {:?}", e),
|
||||||
)
|
)
|
||||||
.into_response();
|
.into_response()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
// 方式2: 返回400错误状态码
|
// 返回400错误状态码
|
||||||
return (
|
(
|
||||||
StatusCode::BAD_REQUEST,
|
StatusCode::BAD_REQUEST,
|
||||||
format!("日期格式错误: {}. 期望格式: YYYYMMDDHHMMSS", e),
|
format!("日期格式错误: {}. 期望格式: YYYYMMDDHHMMSS", e),
|
||||||
)
|
)
|
||||||
.into_response();
|
.into_response()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn download_image(url: &str) -> Result<(Vec<u8>, String), Box<dyn std::error::Error>> {
|
||||||
|
let client = reqwest::Client::new();
|
||||||
|
let response = client.get(url).send().await?;
|
||||||
|
|
||||||
|
if response.status().is_success() {
|
||||||
|
// 检测图像类型
|
||||||
|
let content_type = response
|
||||||
|
.headers()
|
||||||
|
.get("content-type")
|
||||||
|
.and_then(|ct| ct.to_str().ok())
|
||||||
|
.unwrap_or("image/png")
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
let bytes = response.bytes().await?;
|
||||||
|
|
||||||
|
Ok((bytes.to_vec(), content_type.to_string()))
|
||||||
|
} else {
|
||||||
|
Err(format!("HTTP错误: {}", response.status()).into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ pub struct Config {
|
|||||||
pub database_url: String,
|
pub database_url: String,
|
||||||
pub jwt_secret: String,
|
pub jwt_secret: String,
|
||||||
pub port: u16,
|
pub port: u16,
|
||||||
|
pub local_oss: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
@ -18,6 +19,7 @@ impl Config {
|
|||||||
.unwrap_or_else(|_| "3000".to_string())
|
.unwrap_or_else(|_| "3000".to_string())
|
||||||
.parse()
|
.parse()
|
||||||
.unwrap_or(3000),
|
.unwrap_or(3000),
|
||||||
|
local_oss: env::var("LOCAL_OSS")?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ mod config;
|
|||||||
mod db;
|
mod db;
|
||||||
mod model;
|
mod model;
|
||||||
mod service;
|
mod service;
|
||||||
|
mod utils;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
|||||||
@ -1 +1,2 @@
|
|||||||
pub mod inputs;
|
pub mod inputs;
|
||||||
|
pub mod responses;
|
||||||
|
|||||||
19
src/model/responses.rs
Normal file
19
src/model/responses.rs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct TileResponse {
|
||||||
|
pub id: Uuid,
|
||||||
|
pub ingestion_time: DateTime<Utc>,
|
||||||
|
pub data_time: DateTime<Utc>,
|
||||||
|
pub source: String,
|
||||||
|
pub storage_url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct ImageInfo {
|
||||||
|
pub url: String,
|
||||||
|
pub timestamp: DateTime<Utc>,
|
||||||
|
pub source: String,
|
||||||
|
}
|
||||||
@ -1,3 +1,4 @@
|
|||||||
|
use crate::model::responses::TileResponse;
|
||||||
use axum::Error;
|
use axum::Error;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
@ -16,7 +17,7 @@ impl Service {
|
|||||||
&self,
|
&self,
|
||||||
datetime: DateTime<Utc>,
|
datetime: DateTime<Utc>,
|
||||||
area: &str,
|
area: &str,
|
||||||
) -> Result<Vec<u8>, Error> {
|
) -> Result<TileResponse, Error> {
|
||||||
let primitive_datetime = datetime.format("%Y%m%d%H%M%S").to_string();
|
let primitive_datetime = datetime.format("%Y%m%d%H%M%S").to_string();
|
||||||
|
|
||||||
let record = sqlx::query!(
|
let record = sqlx::query!(
|
||||||
@ -37,6 +38,18 @@ impl Service {
|
|||||||
.await
|
.await
|
||||||
.map_err(|e| Error::new(format!("Database error: {}", e)))?;
|
.map_err(|e| Error::new(format!("Database error: {}", e)))?;
|
||||||
|
|
||||||
Ok(Vec::new())
|
match record {
|
||||||
|
Some(row) => Ok(TileResponse {
|
||||||
|
id: row.id,
|
||||||
|
ingestion_time: row.ingestion_time.and_utc(),
|
||||||
|
data_time: row.data_time.and_utc(),
|
||||||
|
source: row.source,
|
||||||
|
storage_url: row.storage_url,
|
||||||
|
}),
|
||||||
|
None => Err(Error::new(format!(
|
||||||
|
"No tile data found for datetime: {} and area: {}",
|
||||||
|
primitive_datetime, area
|
||||||
|
))),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
11
src/utils.rs
Normal file
11
src/utils.rs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
use url::Url;
|
||||||
|
|
||||||
|
pub fn build_url(base: &str, path_segments: &[&str]) -> Result<String, url::ParseError> {
|
||||||
|
let mut url = Url::parse(base)?;
|
||||||
|
{
|
||||||
|
let mut segs = url.path_segments_mut().expect("base URL must be a base");
|
||||||
|
segs.extend(path_segments);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(url.into())
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user