A stateless RESTful API gateway that posts to Bilibili dynamics, receives Aliyun OSS EventBridge webhooks, and refreshes Aliyun CDN. Built with Axum framework, with container support via Docker and CI/CD support via GitHub Actions.
- Stateless architecture - No database, pure HTTP API service
- Bilibili integration - Post dynamics with multipart file upload
- Aliyun OSS EventBridge - Receive and process OSS events via webhooks
- Aliyun CDN refresh - Cache invalidation support
- JWT authentication - ES256 (ECDSA P-256) for protected routes
- OpenAPI documentation - Scalar UI at
/api/scalar - mimalloc allocator - High-performance memory allocation
- Axum 0.8 + Tower middleware
- Reqwest for external APIs
- Utoipa for OpenAPI spec generation
- ES256 JWT (ECDSA P-256) for authentication
- Aliyun V3 signature for OSS EventBridge
The configuration is divided into several main sections:
- Logger
- Server
- Bilibili (API credentials)
- Aliyun (OSS/CDN credentials)
- JWT (ES256 keys)
- Sentry (Optional)
Controls the application's logging behavior.
| Field | Description | Options |
|---|---|---|
enable |
Enable log writing to stdout | true/false |
level |
Set logging level | trace, debug, info, warn, error |
format |
Set logger format | compact, pretty, json |
override_filter |
Override default tracing filter | Any valid tracing filter string |
Configures the web server settings.
[server]
binding = "0.0.0.0"
port = 25150
host = "http://localhost"| Field | Description |
|---|---|
binding |
Server binding address (defaults to "0.0.0.0") |
port |
Port number for the server |
host |
Web server host URL |
Bilibili API credentials for posting dynamics.
[bilibili]
sessdata = "your_bilibili_sessdata_cookie"
bili_jct = "your_bilibili_bili_jct"| Field | Description |
|---|---|
sessdata |
Bilibili SESSDATA cookie value |
bili_jct |
Bilibili bili_jct cookie value (for CSRF) |
Aliyun OSS and CDN credentials.
[aliyun]
access_key_id = "your_aliyun_access_key_id"
access_key_secret = "your_aliyun_access_key_secret"
[aliyun.bucket_url_map]
prts-static = "https://static.prts.wiki/{object_key}"
ak-media = "https://media.prts.wiki/{object_key}"| Field | Description |
|---|---|
access_key_id |
Aliyun Access Key ID |
access_key_secret |
Aliyun Access Key Secret |
bucket_url_map |
Bucket to URL template mapping (optional) |
The {object_key} placeholder in bucket_url_map will be URL-encoded and replaced with the actual object key.
ES256 (ECDSA P-256) keys for API authentication.
[jwt]
private_key = """-----BEGIN EC PRIVATE KEY-----
YOUR_PRIVATE_KEY_HERE
-----END EC PRIVATE KEY-----"""
public_key = """-----BEGIN PUBLIC KEY-----
YOUR_PUBLIC_KEY_HERE
-----END PUBLIC KEY-----"""| Field | Description |
|---|---|
private_key |
ES256 private key (PEM format) |
public_key |
ES256 public key (PEM format) |
# Generate private key (prime256v1 = P-256)
openssl ecparam -genkey -name prime256v1 -noout -out private.pem
# Extract public key
openssl ec -in private.pem -pubout -out public.pemOptional configuration for Sentry error tracking and monitoring.
[sentry]
dsn = "https://your-sentry-dsn@sentry.io/project-id"
traces_sample_rate = 1.0| Field | Description | Required |
|---|---|---|
dsn |
Sentry DSN for error reporting | No |
traces_sample_rate |
Sampling rate for performance traces (0.0-1.0) | No |
| Method | Path | Description |
|---|---|---|
| GET | /api/_ping |
Health check (ping) |
| GET | /api/_health |
Health check (detailed) |
| POST | /api/aliyun/events |
OSS EventBridge webhook |
All protected routes require Authorization: Bearer <token> header.
| Method | Path | Description |
|---|---|---|
| POST | /api/bilibili/createDynamic |
Create Bilibili dynamic with file upload |
| Path | Description |
|---|---|
/api/scalar |
Scalar UI (OpenAPI) |
/api/openapi.json |
OpenAPI specification |
Protected routes use ES256 JWT. Generate a token:
cargo run -- generate-jwt --config config.toml --subject user_idUse the token in requests:
curl -X POST http://localhost:25150/api/bilibili/createDynamic \
-H "Authorization: Bearer <token>" \
-F "file=@/path/to/image.jpg" \
-F "text=Hello Bilibili"EventBridge webhooks use a custom header x-eventbridge-signature-token for authentication, verified using the same JWT verification as Bilibili routes.
# Build the project
cargo build
# Run the server
cargo run -- server --config config.toml
# Generate a JWT token
cargo run -- generate-jwt --config config.toml --subject user_id
# Format code
cargo fmt
# Run linter (must pass in CI)
cargo clippy --all-features -- -D warnings
# Install development tools
just init
# Generate changelog for release
just pre-release <version>- 4-space indentation for Rust files
- 100 character line limit
- Run
cargo fmtbefore committing - Run
cargo clippy --all-features -- -D warnings(CI enforces this)
# Run tests
cargo testNote: Only a few tests exist (aliyun/signature.rs). CI does not run tests automatically.
just pre-release <version>- Generate CHANGELOG.md via git-cliffcargo release <version>- Create git tag- GitHub Action builds and pushes Docker image to GHCR
See the example.toml file for a complete example configuration.
main.rs(15 lines): Sets mimalloc, callsapp::run()lib.rs(11 lines): Public exports:aliyun,app,auth,errorapp.rs(84 lines): CLI parser -server,generate-jwt,version
bilibili_config: BilibiliConfig- API credentialsaliyun_config: AliyunConfig- OSS/CDN credentialsjwt_config: JwtConfig- ES256 private/public keyshttp_client: reqwest::Client- Shared HTTP client- NO database or repository
src/
├── main.rs # CLI entry
├── lib.rs # Public exports
├── app.rs # CLI + server startup
├── config.rs # TOML config
├── state.rs # AppState
├── error.rs # AppError
├── auth.rs # JWT ES256
├── middleware.rs # Tower layers
├── tracing.rs # Logging setup
├── shutdown.rs # Graceful shutdown
├── aliyun/ # OSS signature + CDN
│ ├── cdn.rs
│ └── signature.rs
└── routes/ # HTTP handlers
├── bilibili_handlers.rs
├── aliyun_handlers.rs
└── misc_handlers.rs
MIT