├── migrations ├── .gitkeep ├── 2021-11-30-135749_create_statistics_table │ ├── down.sql │ └── up.sql ├── 2022-02-10-024102_statistics_v2 │ ├── down.sql │ └── up.sql ├── 2023-12-28-131316_statistics_v3 │ ├── down.sql │ └── up.sql └── 2023-12-28-150535_statistics_v4 │ ├── down.sql │ └── up.sql ├── .gitignore ├── diesel.toml ├── src ├── build.rs ├── schema.rs ├── membership_model.rs ├── lib.rs ├── boring_face.rs ├── main.rs ├── statistics_model.rs ├── app_router.rs └── app_model.rs ├── Dockerfile ├── .github └── workflows │ ├── contributors.yml │ ├── cross-compile.yml │ └── docker-build-push.yml ├── Cargo.toml ├── templates ├── join_us.html ├── rank.html ├── index.html └── base.html ├── README.md ├── resources └── membership.json └── Cargo.lock /migrations/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /target 3 | .env 4 | /data -------------------------------------------------------------------------------- /migrations/2021-11-30-135749_create_statistics_table/down.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE `statistics`; -------------------------------------------------------------------------------- /migrations/2022-02-10-024102_statistics_v2/down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE `statistics` RENAME COLUMN unique_visitor TO page_view; -------------------------------------------------------------------------------- /migrations/2022-02-10-024102_statistics_v2/up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE `statistics` RENAME COLUMN page_view TO unique_visitor; -------------------------------------------------------------------------------- /migrations/2023-12-28-131316_statistics_v3/down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE 2 | `statistics` DROP COLUMN latest_referrered_at; -------------------------------------------------------------------------------- /migrations/2023-12-28-131316_statistics_v3/up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE 2 | `statistics` 3 | ADD 4 | latest_referrered_at TIMESTAMP; -------------------------------------------------------------------------------- /migrations/2023-12-28-150535_statistics_v4/down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE 2 | `statistics` RENAME COLUMN latest_referrer_at TO latest_referrered_at; -------------------------------------------------------------------------------- /migrations/2023-12-28-150535_statistics_v4/up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE 2 | `statistics` RENAME COLUMN latest_referrered_at TO latest_referrer_at; -------------------------------------------------------------------------------- /diesel.toml: -------------------------------------------------------------------------------- 1 | # For documentation on how to configure this file, 2 | # see diesel.rs/guides/configuring-diesel-cli 3 | 4 | [print_schema] 5 | file = "src/schema.rs" 6 | -------------------------------------------------------------------------------- /src/build.rs: -------------------------------------------------------------------------------- 1 | use std::process::Command; 2 | fn main() { 3 | let output = Command::new("git") 4 | .args(["rev-parse", "HEAD"]) 5 | .output() 6 | .unwrap(); 7 | let git_hash = String::from_utf8(output.stdout).unwrap(); 8 | println!("cargo:rustc-env=GIT_HASH={}", git_hash); 9 | } 10 | -------------------------------------------------------------------------------- /src/schema.rs: -------------------------------------------------------------------------------- 1 | // @generated automatically by Diesel CLI. 2 | 3 | diesel::table! { 4 | statistics (id) { 5 | id -> Integer, 6 | created_at -> Timestamp, 7 | updated_at -> Timestamp, 8 | membership_id -> BigInt, 9 | unique_visitor -> BigInt, 10 | referrer -> BigInt, 11 | latest_referrer_at -> Timestamp, 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /migrations/2021-11-30-135749_create_statistics_table/up.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE `statistics` ( 2 | id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, 3 | created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, 4 | updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, 5 | membership_id UNSIGNED BIGINT DEFAULT 0 NOT NULL, 6 | page_view UNSIGNED BIGINT DEFAULT 0 NOT NULL, 7 | referrer UNSIGNED BIGINT DEFAULT 0 NOT NULL 8 | ); 9 | CREATE UNIQUE INDEX idx_statistics_membership_id ON `statistics` (membership_id, created_at); 10 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:latest 2 | ARG TARGETPLATFORM 3 | ENV TZ="Asia/Shanghai" 4 | 5 | RUN export DEBIAN_FRONTEND="noninteractive" && apt update && apt install -y ca-certificates tzdata \ 6 | libsqlite3-dev && \ 7 | update-ca-certificates && \ 8 | ln -fs /usr/share/zoneinfo/$TZ /etc/localtime && \ 9 | dpkg-reconfigure tzdata 10 | 11 | WORKDIR /webapp 12 | COPY ./artifact/$TARGETPLATFORM/naive ./naive 13 | RUN chmod +x ./naive 14 | COPY ./migrations ./migrations 15 | COPY ./resources ./resources 16 | COPY ./templates ./templates 17 | 18 | VOLUME ["/webapp/data"] 19 | CMD ["/webapp/naive"] 20 | -------------------------------------------------------------------------------- /src/membership_model.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use crate::statistics_model::Statistics; 4 | 5 | #[derive(Deserialize, Clone, Debug, Serialize)] 6 | pub struct Membership { 7 | #[serde(skip_deserializing)] 8 | pub id: i64, 9 | pub domain: String, 10 | pub name: String, 11 | pub icon: String, 12 | pub description: String, 13 | pub github_username: String, 14 | pub hidden: Option, 15 | } 16 | 17 | #[derive(Deserialize, Clone, Serialize)] 18 | pub struct RankAndMembership { 19 | pub rank: Statistics, 20 | pub membership: Membership, 21 | } 22 | -------------------------------------------------------------------------------- /.github/workflows/contributors.yml: -------------------------------------------------------------------------------- 1 | name: contributors 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | 7 | jobs: 8 | contributors: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Generate Contributors Images 12 | uses: jaywcjlove/github-action-contributors@main 13 | id: contributors 14 | with: 15 | filter-author: (renovate\[bot\]|renovate-bot|dependabot\[bot\]) 16 | hideName: 'false' # Hide names in htmlTable 17 | avatarSize: 66 # Set the avatar size. 18 | truncate: 6 19 | 20 | - name: Modify htmlTable README.md 21 | uses: jaywcjlove/github-action-modify-file-content@main 22 | with: 23 | message: update contributors[no ci] 24 | openDelimiter: '' 25 | closeDelimiter: '' 26 | path: README.md 27 | body: '${{steps.contributors.outputs.htmlList}}' -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | 3 | use chrono::{NaiveDateTime, Utc}; 4 | use chrono_tz::Asia::Shanghai; 5 | use diesel::{ 6 | r2d2::{ConnectionManager, Pool}, 7 | SqliteConnection, 8 | }; 9 | use lazy_static::lazy_static; 10 | 11 | pub mod app_model; 12 | pub mod app_router; 13 | pub mod boring_face; 14 | pub mod membership_model; 15 | pub mod schema; 16 | pub mod statistics_model; 17 | 18 | extern crate diesel; 19 | 20 | pub const GIT_HASH: &str = env!("GIT_HASH"); 21 | 22 | // 系统域名,忽略 referrer 计数 23 | lazy_static! { 24 | static ref SYSTEM_DOMAIN: String = env::var("SYSTEM_DOMAIN").unwrap(); 25 | } 26 | 27 | pub type DbPool = Pool>; 28 | 29 | pub fn establish_connection(database_url: &str) -> DbPool { 30 | let manager = ConnectionManager::::new(database_url); 31 | Pool::builder() 32 | .max_size(5) 33 | .build(manager) 34 | .unwrap_or_else(|_| panic!("Error connecting to {}", database_url)) 35 | } 36 | 37 | pub fn now_shanghai() -> NaiveDateTime { 38 | Utc::now().with_timezone(&Shanghai).naive_local() 39 | } 40 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | build = "src/build.rs" 3 | edition = "2021" 4 | name = "naive" 5 | version = "0.1.0" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | anyhow = "1.0.44" 11 | askama = "0.11.0" 12 | axum = { version = "0.4.4", features = ["headers", "ws"] } 13 | chrono = { version = "0.4", features = ["serde"] } 14 | chrono-tz = "0.6.1" 15 | diesel = { version = "2.0.0-rc.0", features = [ 16 | "bigdecimal", 17 | "chrono", 18 | "r2d2", 19 | "sqlite", 20 | ] } 21 | diesel_migrations = "2.0.0-rc.0" 22 | dotenv = "0.15.0" 23 | headers = "0.3.5" 24 | lazy_static = "1.4.0" 25 | r-cache = "0.4.4" 26 | regex = "1.5.4" 27 | serde = { version = "1.0", features = ["derive"] } 28 | serde_json = "1.0" 29 | serde_repr = "0.1" 30 | tokio = { version = "1.0", features = ["full"] } 31 | tracing = "0.1" 32 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 33 | url = "2.2.2" 34 | 35 | [dependencies.libsqlite3-sys] 36 | # https://github.com/diesel-rs/diesel/issues/2943 37 | features = ["bundled"] 38 | version = ">=0.17.2, <0.25.0" 39 | -------------------------------------------------------------------------------- /templates/join_us.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}加入我们{% endblock %} 4 | 5 | {% block content %} 6 |
7 |

入会说明

8 |
9 | 在 这里 修改 10 | resources/membership.json,将你的站点添加进去,然后将 Badge 11 | 添加至您站点的底部(记得修改 [domain] 为你添加的域名)。牙齿颜色的进度代表当日访问量的多少,图标及站点配色采用了人民币配色。 12 |
13 |

14 | 15 | Badge:(修改 height 调整大小) 16 |
17 | <a title="无聊湾 🥱 The Boring Bay" href="https://boringbay.com"><img height="18px" src="https://boringbay.com/api/badge/[domain]"></img></a>
18 |
19 | 20 |

21 |

22 | 23 | Icon(仅展示无法记录访客): 24 |
25 | <a href="https://boringbay.com"><img height="18px" src="https://boringbay.com/api/icon/[domain]"></img></a>
26 |
27 | 28 |

29 |

30 | 31 | Favicon:(仅展示无法记录访客,可用做站点 favicon,适配了日间/夜间主题,颜色是inverse的) 32 |
33 | <a href="https://boringbay.com"><img height="18px" src="https://boringbay.com/api/favicon/[domain]"></img></a>
34 |
35 | 36 |

37 |
38 |
39 | {% endblock %} -------------------------------------------------------------------------------- /.github/workflows/cross-compile.yml: -------------------------------------------------------------------------------- 1 | name: Cross compile 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | paths-ignore: 7 | - "docker-compose.yaml" 8 | 9 | jobs: 10 | compile: 11 | runs-on: ubuntu-latest 12 | strategy: 13 | matrix: 14 | include: 15 | - target: x86_64-unknown-linux-gnu 16 | docker: linux/amd64 17 | cross: false 18 | binary: naive 19 | - target: aarch64-unknown-linux-gnu 20 | docker: linux/arm64 21 | cross: true 22 | binary: naive 23 | steps: 24 | - uses: actions/checkout@v2 25 | 26 | - name: Setup cargo cache 27 | uses: actions/cache@v3 28 | with: 29 | path: | 30 | ~/.cargo/registry 31 | ~/.cargo/git 32 | key: ${{ runner.os }}-${{ matrix.target }}-cargo-${{ hashFiles('**/Cargo.lock') }} 33 | restore-keys: | 34 | ${{ runner.os }}-${{ matrix.target }}-cargo- 35 | 36 | - name: install toolchain 37 | uses: actions-rs/toolchain@v1 38 | with: 39 | toolchain: stable 40 | target: ${{ matrix.target }} 41 | override: true 42 | 43 | - name: compile 44 | uses: actions-rs/cargo@v1 45 | with: 46 | use-cross: ${{ matrix.cross }} 47 | command: build 48 | args: --release --target=${{ matrix.target }} 49 | 50 | - name: Move target 51 | run: | 52 | mkdir -p artifact/${{ matrix.docker }}/ 53 | mv target/${{ matrix.target }}/release/${{ matrix.binary }} artifact/${{ matrix.docker }}/ 54 | 55 | - name: Upload artifact 56 | uses: actions/upload-artifact@v4 57 | with: 58 | name: ${{ matrix.docker == 'linux/amd64' && 'linux-amd64-artifact' || 'linux-arm64-artifact' }} 59 | path: artifact/ 60 | 61 | -------------------------------------------------------------------------------- /.github/workflows/docker-build-push.yml: -------------------------------------------------------------------------------- 1 | name: Docker build and push 2 | 3 | on: 4 | workflow_run: 5 | workflows: ["Cross compile"] 6 | branches: [main] 7 | types: 8 | - completed 9 | 10 | jobs: 11 | docker-build-push: 12 | if: ${{ github.event.workflow_run.conclusion == 'success' }} 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v3 16 | 17 | - name: Log in to the GHCR 18 | uses: docker/login-action@v2 19 | with: 20 | registry: ghcr.io 21 | username: ${{ github.repository_owner }} 22 | password: ${{ secrets.GITHUB_TOKEN }} 23 | 24 | - name: Set up QEMU 25 | uses: docker/setup-qemu-action@v2 26 | 27 | - name: Set up Docker Buildx 28 | uses: docker/setup-buildx-action@v2 29 | 30 | - name: Download artifact 31 | uses: dawidd6/action-download-artifact@v2 32 | with: 33 | github_token: ${{ secrets.GITHUB_TOKEN }} 34 | workflow: ${{ github.event.workflow_run.workflow_id }} 35 | workflow_conclusion: success 36 | check_artifacts: true 37 | path: artifact 38 | 39 | - name: Rearrange artifacts 40 | run: | 41 | # 如果该目录存在,则移动其下文件 42 | if [ -d "artifact/linux-amd64-artifact/linux/amd64" ]; then 43 | mkdir -p artifact/linux/amd64 44 | mv artifact/linux-amd64-artifact/linux/amd64/* artifact/linux/amd64/ 45 | fi 46 | 47 | if [ -d "artifact/linux-arm64-artifact/linux/arm64" ]; then 48 | mkdir -p artifact/linux/arm64 49 | mv artifact/linux-arm64-artifact/linux/arm64/* artifact/linux/arm64/ 50 | fi 51 | 52 | # 清理不再需要的空目录 53 | rm -rf artifact/linux-amd64-artifact 54 | rm -rf artifact/linux-arm64-artifact 55 | 56 | - name: Build Docker Image And Push 57 | uses: docker/build-push-action@v3 58 | with: 59 | context: . 60 | file: ./Dockerfile 61 | platforms: linux/amd64,linux/arm64 62 | push: true 63 | tags: ghcr.io/cantoblanco/boringbay 64 | -------------------------------------------------------------------------------- /src/boring_face.rs: -------------------------------------------------------------------------------- 1 | static SVG_HEADER: &str = r###" 2 | 3 | 4 | 28 | 29 | #svg_border# 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | "###; 38 | static SVG_BORDER: &str = r###" 39 | 40 | #site_name# 🥱 41 | UV: #site_uv# Referer: #site_referrer# Level: #site_rank# 42 | "###; 43 | static SVG_FOOTER: &str = r###" 44 | 45 | 46 | "###; 47 | 48 | pub struct BoringFace { 49 | fill_white: String, 50 | fill_black: String, 51 | with_border: bool, 52 | } 53 | 54 | impl BoringFace { 55 | pub fn new(fill_white: String, fill_black: String, with_border: bool) -> Self { 56 | Self { 57 | fill_white, 58 | fill_black, 59 | with_border, 60 | } 61 | } 62 | 63 | pub fn render_svg(&self, name: &str, uv: i64, rv: i64, rank: i64) -> String { 64 | assert!((1..=10).contains(&rank)); 65 | let mut content = SVG_HEADER 66 | .replace("#fill_white#", &self.fill_white) 67 | .replace("#fill_black#", &self.fill_black); 68 | 69 | for i in 0..10 { 70 | content.push_str(&format!( 71 | "", 72 | match i.lt(&rank) { 73 | true => "class=\"fill-black\"", 74 | false => "fill=\"#d55f6f\"", 75 | }, 76 | 35.5 + (i as f64) * 4f64 77 | )); 78 | } 79 | 80 | content.push_str(SVG_FOOTER); 81 | 82 | content = content.replace( 83 | "#svg_viewport#", 84 | match self.with_border { 85 | true => "viewBox=\"-3.412 -2.5 584.261 115.604\"", 86 | _ => "viewBox=\"6 6.1 98 97\"", 87 | }, 88 | ); 89 | 90 | if self.with_border { 91 | content = content.replace( 92 | "#svg_border#", 93 | &SVG_BORDER 94 | .replace("#site_name#", name) 95 | .replace("#site_uv#", &uv.to_string()) 96 | .replace("#site_referrer#", &rv.to_string()) 97 | .replace("#site_rank#", &rank.to_string()), 98 | ); 99 | } 100 | 101 | content 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use axum::{routing::get, AddExtensionLayer, Router}; 2 | use chrono::{NaiveDateTime, NaiveTime}; 3 | use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness}; 4 | use dotenv::dotenv; 5 | use naive::{ 6 | app_model::{Context, DynContext}, 7 | app_router::{ 8 | home_page, join_us_page, rank_page, show_badge, show_favicon, show_icon, ws_upgrade, 9 | }, 10 | establish_connection, now_shanghai, 11 | statistics_model::Statistics, 12 | DbPool, 13 | }; 14 | use std::{env, net::SocketAddr, sync::Arc}; 15 | use tokio::signal; 16 | 17 | pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!("./migrations/"); 18 | 19 | #[tokio::main] 20 | async fn main() { 21 | dotenv().ok(); 22 | tracing_subscriber::fmt::init(); 23 | 24 | let db_pool: DbPool = establish_connection(&env::var("DATABASE_URL").unwrap()); 25 | 26 | tracing::info!( 27 | "migration {:?}", 28 | db_pool 29 | .get() 30 | .unwrap() 31 | .run_pending_migrations(MIGRATIONS) 32 | .unwrap() 33 | ); 34 | 35 | let context = Arc::new(Context::default(db_pool).await) as DynContext; 36 | 37 | // 定时存入数据库 38 | let ctx_clone = context.clone(); 39 | tokio::spawn(async move { 40 | ctx_clone.save_per_5_minutes().await; 41 | }); 42 | 43 | let ctx_clone_for_shutdown = context.clone(); 44 | 45 | let app = Router::new() 46 | .nest( 47 | "/api", 48 | Router::new() 49 | .route("/badge/:domain", get(show_badge)) 50 | .route("/favicon/:domain", get(show_favicon)) 51 | .route("/icon/:domain", get(show_icon)) 52 | .route("/ws", get(ws_upgrade)), 53 | ) 54 | .route("/", get(home_page)) 55 | .route("/join-us", get(join_us_page)) 56 | .route("/rank", get(rank_page)) 57 | .layer(AddExtensionLayer::new(context)); 58 | 59 | let addr = SocketAddr::from(([0, 0, 0, 0], 3000)); 60 | tracing::debug!("listening on {}", addr); 61 | axum::Server::bind(&addr) 62 | .serve(app.into_make_service()) 63 | .with_graceful_shutdown(shutdown_signal(ctx_clone_for_shutdown)) 64 | .await 65 | .unwrap(); 66 | } 67 | 68 | async fn shutdown_signal(ctx: Arc) { 69 | let ctrl_c = async { 70 | signal::ctrl_c() 71 | .await 72 | .expect("failed to install Ctrl+C handler"); 73 | }; 74 | 75 | #[cfg(unix)] 76 | let terminate = async { 77 | signal::unix::signal(signal::unix::SignalKind::terminate()) 78 | .expect("failed to install signal handler") 79 | .recv() 80 | .await; 81 | }; 82 | 83 | #[cfg(not(unix))] 84 | let terminate = std::future::pending::<()>(); 85 | 86 | tokio::select! { 87 | _ = ctrl_c => {}, 88 | _ = terminate => {}, 89 | } 90 | 91 | println!("signal received, running cleanup tasks.."); 92 | 93 | let _today = NaiveDateTime::new(now_shanghai().date(), NaiveTime::from_hms(0, 0, 0)); 94 | let page_view_read = ctx.unique_visitor.read().await; 95 | let referrer_read = ctx.referrer.read().await; 96 | ctx.id2member.keys().for_each(|id| { 97 | let uv = *page_view_read.get(id).unwrap_or(&(0, NaiveDateTime::from_timestamp(0, 0))); 98 | let referrer = *referrer_read.get(id).unwrap_or(&(0, NaiveDateTime::from_timestamp(0, 0))); 99 | Statistics::insert_or_update( 100 | ctx.db_pool.get().unwrap(), 101 | &Statistics { 102 | created_at: _today, 103 | membership_id: *id, 104 | unique_visitor: uv.0, 105 | updated_at: uv.1, 106 | referrer: referrer.0, 107 | latest_referrer_at: referrer.1, 108 | id: 0, 109 | }, 110 | ) 111 | .unwrap(); 112 | }) 113 | } 114 | -------------------------------------------------------------------------------- /templates/rank.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}排行{% endblock %} 4 | 5 | {% block content %} 6 |
7 |

总排行

8 |
9 | 10 | 11 | 12 | 13 | 14 | 17 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | {% for r in rank %} 27 | 28 | 29 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | {% endfor %} 43 | 44 |
排名站点UV 15 |

独立访客

16 |
RV 18 |

来访访客

19 |
最后链入最后受访加入时间
{{ loop.index }} 30 | 33 | {{ r.membership.name|e }} 34 | 35 | {{ r.rank.unique_visitor }}{{ r.rank.referrer }}{{ r.rank.latest_referrer_at.format("%Y-%m-%d %H:%M:%S") }}{{ r.rank.updated_at.format("%Y-%m-%d %H:%M:%S") }}{{ r.rank.created_at.format("%Y-%m-%d %H:%M:%S") }}
45 |
46 | 47 |

即将移除

48 |
49 | 50 | 51 | 52 | 53 | 54 | 57 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | {% for r in to_be_remove %} 67 | 68 | 73 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | {% endfor %} 89 | 90 |
ID站点UV 55 |

独立访客

56 |
RV 58 |

来访访客

59 |
最后链入最后受访加入时间
69 | 70 | {{ r.membership.id }} 71 | 72 | 74 | 75 | 78 | {{ r.membership.name|e }} 79 | 80 | 81 | {{ r.rank.unique_visitor }}{{ r.rank.referrer }}{{ r.rank.latest_referrer_at.format("%Y-%m-%d %H:%M:%S") }}{{ r.rank.updated_at.format("%Y-%m-%d %H:%M:%S") }}{{ r.rank.created_at.format("%Y-%m-%d %H:%M:%S") }}
91 |
92 |
93 | {% endblock %} -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}首页{% endblock %} 4 | 5 | {% block content %} 6 |
7 |

今日无聊

8 |
9 | {% for m in membership %} 10 |
11 |
12 |
13 | 14 |

16 | UV{{uv.get(m.id).cloned().unwrap_or_default()}} 17 |
18 | RV{{referrer.get(m.id).cloned().unwrap_or_default()}} 19 |
20 | Lv.{{level.get(m.id).cloned().unwrap_or_default()}} 21 |

22 |
23 |
24 | 26 | {{ m.name|e }} 27 |
28 | {{ m.description|e }}
29 | 31 | {{ m.github_username|e }} 32 |
33 |
34 |
35 | {% endfor %} 36 |
37 | 38 | 39 |

30 天排行

40 |
41 | 42 | 43 | 44 | 45 | 46 | 49 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | {% for r in rank %} 59 | 60 | 61 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | {% endfor %} 75 | 76 |
排名站点UV 47 |

独立访客

48 |
RV 50 |

来访访客

51 |
最后链入最后受访加入时间
{{ loop.index }} 62 | 65 | {{ r.membership.name|e }} 66 | 67 | {{ r.rank.unique_visitor }}{{ r.rank.referrer }}{{ r.rank.latest_referrer_at.format("%Y-%m-%d %H:%M:%S") }}{{ r.rank.updated_at.format("%Y-%m-%d %H:%M:%S") }}{{ r.rank.created_at.format("%Y-%m-%d %H:%M:%S") }}
77 |
78 | 79 |

即将移除

80 |
81 | 82 | 83 | 84 | 85 | 86 | 89 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | {% for r in to_be_remove %} 99 | 100 | 105 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | {% endfor %} 121 | 122 |
ID站点UV 87 |

独立访客

88 |
RV 90 |

来访访客

91 |
最后链入最后受访加入时间
101 | 102 | {{ r.membership.id }} 103 | 104 | 106 | 107 | 110 | {{ r.membership.name|e }} 111 | 112 | 113 | {{ r.rank.unique_visitor }}{{ r.rank.referrer }}{{ r.rank.latest_referrer_at.format("%Y-%m-%d %H:%M:%S") }}{{ r.rank.updated_at.format("%Y-%m-%d %H:%M:%S") }}{{ r.rank.created_at.format("%Y-%m-%d %H:%M:%S") }}
123 |
124 |
125 | {% endblock %} -------------------------------------------------------------------------------- /src/statistics_model.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Sub; 2 | 3 | use crate::now_shanghai; 4 | use crate::schema::statistics::{self, dsl::*}; 5 | use anyhow::anyhow; 6 | use chrono::{Duration, NaiveDateTime, NaiveTime}; 7 | use diesel::dsl::sql; 8 | use diesel::r2d2::{ConnectionManager, PooledConnection}; 9 | use diesel::sqlite::Sqlite; 10 | use diesel::{debug_query, prelude::*}; 11 | use diesel::{Queryable, SqliteConnection}; 12 | use tracing::debug; 13 | 14 | #[derive(Queryable, Debug, Clone, Insertable, serde::Serialize, serde::Deserialize)] 15 | #[diesel(table_name = statistics)] 16 | pub struct Statistics { 17 | pub id: i32, 18 | pub created_at: NaiveDateTime, 19 | pub updated_at: NaiveDateTime, 20 | pub membership_id: i64, 21 | pub unique_visitor: i64, 22 | pub referrer: i64, 23 | pub latest_referrer_at: NaiveDateTime, 24 | } 25 | 26 | impl Statistics { 27 | pub fn insert_or_update( 28 | mut conn: PooledConnection>, 29 | stat: &Statistics, 30 | ) -> Result { 31 | let statement = diesel::insert_into(statistics) 32 | .values(( 33 | created_at.eq(stat.created_at), 34 | updated_at.eq(stat.updated_at), 35 | membership_id.eq(stat.membership_id), 36 | unique_visitor.eq(stat.unique_visitor), 37 | referrer.eq(stat.referrer), 38 | latest_referrer_at.eq(stat.latest_referrer_at), 39 | )) 40 | .on_conflict((membership_id, created_at)) 41 | .do_update() 42 | .set(( 43 | unique_visitor.eq(stat.unique_visitor), 44 | referrer.eq(stat.referrer), 45 | updated_at.eq(stat.updated_at), 46 | latest_referrer_at.eq(stat.latest_referrer_at), 47 | )); 48 | debug!("sql: {}", debug_query::(&statement)); 49 | statement.execute(&mut conn) 50 | } 51 | 52 | pub fn today( 53 | conn: PooledConnection>, 54 | ) -> Result, anyhow::Error> { 55 | load_statistics_by_created_at( 56 | conn, 57 | NaiveDateTime::new(now_shanghai().date(), NaiveTime::from_hms(0, 0, 0)), 58 | ) 59 | } 60 | 61 | pub fn prev_day_rank_avg(conn: PooledConnection>) -> i64 { 62 | let res = load_statistics_by_created_at( 63 | conn, 64 | NaiveDateTime::new(now_shanghai().date(), NaiveTime::from_hms(0, 0, 0)) 65 | .sub(Duration::hours(24)), 66 | ); 67 | if let Ok(res) = res { 68 | let mut sum = 0; 69 | let mut count = 0; 70 | res.iter().for_each(|s| { 71 | let view = s.referrer + s.unique_visitor; 72 | sum += view; 73 | if view > 0 { 74 | count += 1; 75 | } 76 | }); 77 | if count > 0 { 78 | let rank_svg = sum / count / 10; 79 | if rank_svg > 0 { 80 | return rank_svg; 81 | } 82 | } 83 | } 84 | 1 85 | } 86 | 87 | pub fn rank_between( 88 | mut conn: PooledConnection>, 89 | start: NaiveDateTime, 90 | end: NaiveDateTime, 91 | ) -> Result, anyhow::Error> { 92 | let res = statistics 93 | .select(( 94 | membership_id, 95 | sql::("MIN(created_at) as m_created_at"), 96 | sql::("SUM(unique_visitor) as s_unique_visitor"), 97 | sql::("SUM(referrer) as s_referrer"), 98 | )) 99 | .filter(created_at.between(start, end)) 100 | .group_by(membership_id) 101 | .order_by(sql::("s_referrer DESC")) 102 | .then_order_by(sql::("s_unique_visitor DESC")) 103 | .load::<(i64, NaiveDateTime, i64, i64)>(&mut conn); 104 | 105 | let updated_at_list = statistics 106 | .select(( 107 | membership_id, 108 | sql::("MAX(updated_at) as m_updated_at"), 109 | )) 110 | .filter(updated_at.is_not_null().and(unique_visitor.gt(0))) 111 | .group_by(membership_id) 112 | .order(sql::("m_updated_at")) 113 | .load::<(i64, NaiveDateTime)>(&mut conn); 114 | 115 | let id_to_updated_at = updated_at_list 116 | .unwrap_or(Vec::new()) 117 | .iter() 118 | .map(|s| (s.0, s.1)) 119 | .collect::>(); 120 | 121 | let latest_referrer_at_list = statistics 122 | .select(( 123 | membership_id, 124 | sql::( 125 | "MAX(latest_referrer_at) as m_latest_referrer_at", 126 | ), 127 | )) 128 | .filter(latest_referrer_at.is_not_null().and(referrer.gt(0))) 129 | .group_by(membership_id) 130 | .order(sql::("m_latest_referrer_at")) 131 | .load::<(i64, NaiveDateTime)>(&mut conn); 132 | 133 | let id_to_latest_referrer_at = latest_referrer_at_list 134 | .unwrap_or(Vec::new()) 135 | .iter() 136 | .map(|s| (s.0, s.1)) 137 | .collect::>(); 138 | 139 | match res { 140 | Ok(all) => { 141 | let mut result = Vec::new(); 142 | all.iter().for_each(|s| { 143 | result.push(Statistics { 144 | id: 0, 145 | created_at: s.1, 146 | updated_at: id_to_updated_at 147 | .get(&s.0) 148 | .unwrap_or(&NaiveDateTime::from_timestamp(0, 0)) 149 | .to_owned(), 150 | latest_referrer_at: id_to_latest_referrer_at 151 | .get(&s.0) 152 | .unwrap_or(&NaiveDateTime::from_timestamp(0, 0)) 153 | .to_owned(), 154 | membership_id: s.0, 155 | unique_visitor: s.2, 156 | referrer: s.3, 157 | }) 158 | }); 159 | Ok(result) 160 | } 161 | Err(e) => Err(anyhow!("{:?}", e)), 162 | } 163 | } 164 | 165 | pub fn all( 166 | mut conn: PooledConnection>, 167 | ) -> Result, anyhow::Error> { 168 | let res = statistics.load::(&mut conn); 169 | match res { 170 | Ok(all) => Ok(all), 171 | Err(e) => Err(anyhow!("{:?}", e)), 172 | } 173 | } 174 | } 175 | 176 | fn load_statistics_by_created_at( 177 | mut conn: PooledConnection>, 178 | _created_at: NaiveDateTime, 179 | ) -> Result, anyhow::Error> { 180 | debug!( 181 | "sql: {}", 182 | debug_query::(&statistics.filter(created_at.eq(_created_at))) 183 | ); 184 | let res = statistics 185 | .filter(created_at.eq(_created_at)) 186 | .load::(&mut conn); 187 | match res { 188 | Ok(all) => Ok(all), 189 | Err(e) => Err(anyhow!("{:?}", e)), 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {% block title %}{% endblock %} | 无聊湾 🥱 The Boring Bay 9 | 10 | 13 | 16 | 18 | 62 | 63 | 64 | 65 |
66 |
67 | 81 | 82 |
83 |
84 | {% block content %}{% endblock %} 85 |
86 | 87 |
88 | QQ交流群:136231667(仅限会员加入) 89 |
90 | 91 |
92 | 93 |
94 | 生命的终极就是无聊,一般的流行文化很容易过时,但是无聊的终极文化永远伴随人类。—— @wdctll 95 |
96 | 97 | 102 |
103 | 104 |
105 | 106 | 109 | 110 | 178 | 179 | 180 | 181 | -------------------------------------------------------------------------------- /src/app_router.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, sync::Arc, time::Duration}; 2 | 3 | use anyhow::anyhow; 4 | use askama::Template; 5 | use axum::{ 6 | extract::{ 7 | ws::{Message, WebSocket}, 8 | Extension, Path, WebSocketUpgrade, 9 | }, 10 | http::StatusCode, 11 | response::{Headers, Html, IntoResponse, Response}, 12 | }; 13 | use chrono::NaiveDateTime; 14 | use headers::HeaderMap; 15 | use tokio::select; 16 | 17 | use crate::{ 18 | app_model::{Context, DynContext}, 19 | boring_face::BoringFace, 20 | membership_model::{Membership, RankAndMembership}, 21 | now_shanghai, GIT_HASH, 22 | }; 23 | 24 | pub async fn ws_upgrade( 25 | Extension(ctx): Extension, 26 | ws: WebSocketUpgrade, 27 | ) -> impl IntoResponse { 28 | ws.on_upgrade(|socket| handle_socket(ctx, socket)) 29 | } 30 | 31 | async fn handle_socket(ctx: Arc, mut socket: WebSocket) { 32 | let mut rx = ctx.visitor_rx.clone(); 33 | let mut interval = tokio::time::interval(Duration::from_secs(8)); 34 | 35 | loop { 36 | select! { 37 | Ok(()) = rx.changed() => { 38 | let msg = rx.borrow().to_string(); 39 | let res = socket.send(Message::Text(msg.clone())).await; 40 | if res.is_err() { 41 | break; 42 | } 43 | } 44 | _ = interval.tick() => { 45 | let res = socket.send(Message::Ping(vec![])).await; 46 | if res.is_err() { 47 | break; 48 | } 49 | } 50 | } 51 | } 52 | } 53 | 54 | pub async fn show_badge( 55 | Path(mut domain): Path, 56 | headers: HeaderMap, 57 | Extension(ctx): Extension, 58 | ) -> Response { 59 | let mut v_type = Some(crate::app_model::VisitorType::Badge); 60 | 61 | let domain_referrer = get_domain_from_referrer(&headers).unwrap_or("".to_string()); 62 | if domain_referrer.ne(&domain) { 63 | if domain.eq("[domain]") { 64 | domain = domain_referrer; 65 | } else { 66 | v_type = None; 67 | } 68 | } 69 | 70 | let tend = ctx.boring_visitor(v_type, &domain, &headers).await; 71 | if tend.is_err() { 72 | return ( 73 | StatusCode::NOT_FOUND, 74 | Headers([("content-type", "text/plain")]), 75 | tend.err().unwrap().to_string(), 76 | ) 77 | .into_response(); 78 | } 79 | 80 | render_svg(tend.unwrap(), &ctx.badge).await 81 | } 82 | 83 | pub async fn show_favicon( 84 | Path(domain): Path, 85 | headers: HeaderMap, 86 | Extension(ctx): Extension, 87 | ) -> Response { 88 | let tend = ctx 89 | .boring_visitor(Some(crate::app_model::VisitorType::ICON), &domain, &headers) 90 | .await; 91 | if tend.is_err() { 92 | return ( 93 | StatusCode::NOT_FOUND, 94 | Headers([("content-type", "text/plain")]), 95 | tend.err().unwrap().to_string(), 96 | ) 97 | .into_response(); 98 | } 99 | render_svg(tend.unwrap(), &ctx.favicon).await 100 | } 101 | 102 | pub async fn show_icon( 103 | Path(domain): Path, 104 | headers: HeaderMap, 105 | Extension(ctx): Extension, 106 | ) -> Response { 107 | let tend = ctx 108 | .boring_visitor(Some(crate::app_model::VisitorType::ICON), &domain, &headers) 109 | .await; 110 | if tend.is_err() { 111 | return ( 112 | StatusCode::NOT_FOUND, 113 | Headers([("content-type", "text/plain")]), 114 | tend.err().unwrap().to_string(), 115 | ) 116 | .into_response(); 117 | } 118 | 119 | render_svg(tend.unwrap(), &ctx.icon).await 120 | } 121 | 122 | #[derive(Template)] 123 | #[template(path = "index.html")] 124 | struct HomeTemplate { 125 | version: String, 126 | membership: Vec, 127 | uv: HashMap, 128 | referrer: HashMap, 129 | rank: Vec, 130 | to_be_remove: Vec, 131 | level: HashMap, 132 | } 133 | 134 | pub async fn home_page( 135 | Extension(ctx): Extension, 136 | headers: HeaderMap, 137 | ) -> Result, String> { 138 | let domain = get_domain_from_referrer(&headers); 139 | if domain.is_ok() { 140 | let _ = ctx 141 | .boring_visitor( 142 | Some(crate::app_model::VisitorType::Referer), 143 | &domain.unwrap(), 144 | &headers, 145 | ) 146 | .await; 147 | } 148 | let referrer_read = ctx.referrer.read().await; 149 | let uv_read = ctx.unique_visitor.read().await; 150 | 151 | let mut level: HashMap = HashMap::new(); 152 | let mut rank_vec: Vec<(i64, NaiveDateTime, i64)> = Vec::new(); 153 | 154 | for k in ctx.id2member.keys() { 155 | let uv = uv_read 156 | .get(k) 157 | .unwrap_or(&(0, NaiveDateTime::from_timestamp(0, 0))) 158 | .to_owned(); 159 | let rv = referrer_read 160 | .get(k) 161 | .unwrap_or(&(0, NaiveDateTime::from_timestamp(0, 0))) 162 | .to_owned(); 163 | if uv.0 > 0 || rv.0 > 0 { 164 | rank_vec.push((k.to_owned(), rv.1, uv.0)); 165 | level.insert(k.to_owned(), ctx.get_tend_from_uv_and_rv(uv.0, rv.0).await); 166 | } 167 | } 168 | 169 | rank_vec.sort_by(|a, b| match b.1.cmp(&a.1) { 170 | std::cmp::Ordering::Equal => b.2.cmp(&a.2), 171 | _ => b.1.cmp(&a.1), 172 | }); 173 | 174 | let mut membership = Vec::new(); 175 | for v in rank_vec { 176 | membership.push(ctx.id2member.get(&v.0).unwrap().to_owned()); 177 | } 178 | 179 | let mut rank_and_membership_to_be_remove = Vec::new(); 180 | let mut rank_and_membership = Vec::new(); 181 | 182 | let monthly_rank = ctx.monthly_rank.read().await.to_owned(); 183 | monthly_rank 184 | .iter() 185 | .filter(|r| ctx.id2member.contains_key(&r.membership_id)) 186 | .for_each(|r| { 187 | if rank_and_membership.len() >= 10 188 | || r.updated_at < now_shanghai() - chrono::Duration::days(30) 189 | { 190 | return; 191 | } 192 | let m = ctx.id2member.get(&r.membership_id).unwrap().to_owned(); 193 | rank_and_membership.push(RankAndMembership { 194 | rank: r.to_owned(), 195 | membership: m, 196 | }); 197 | }); 198 | 199 | let rank = ctx.rank.read().await.to_owned(); 200 | rank.iter() 201 | .filter(|r| ctx.id2member.contains_key(&r.membership_id)) 202 | .for_each(|r| { 203 | if r.updated_at < now_shanghai() - chrono::Duration::days(30) { 204 | let m = ctx.id2member.get(&r.membership_id).unwrap().to_owned(); 205 | rank_and_membership_to_be_remove.push(RankAndMembership { 206 | rank: r.to_owned(), 207 | membership: m, 208 | }); 209 | } 210 | }); 211 | 212 | let tpl = HomeTemplate { 213 | membership, 214 | uv: uv_read 215 | .iter() 216 | .map(|(k, v)| (k.to_owned(), v.0)) 217 | .collect::>(), 218 | referrer: referrer_read 219 | .iter() 220 | .map(|(k, v)| (k.to_owned(), v.0)) 221 | .collect::>(), 222 | rank: rank_and_membership, 223 | to_be_remove: rank_and_membership_to_be_remove, 224 | level, 225 | version: GIT_HASH[0..8].to_string(), 226 | }; 227 | let html = tpl.render().map_err(|err| err.to_string())?; 228 | Ok(Html(html)) 229 | } 230 | 231 | #[derive(Template)] 232 | #[template(path = "join_us.html")] 233 | struct JoinUsTemplate { 234 | version: String, 235 | } 236 | 237 | pub async fn join_us_page() -> Result, String> { 238 | let tpl = JoinUsTemplate { 239 | version: GIT_HASH[0..8].to_string(), 240 | }; 241 | let html = tpl.render().map_err(|err| err.to_string())?; 242 | Ok(Html(html)) 243 | } 244 | 245 | #[derive(Template)] 246 | #[template(path = "rank.html")] 247 | struct RankTemplate { 248 | version: String, 249 | rank: Vec, 250 | to_be_remove: Vec, 251 | } 252 | 253 | pub async fn rank_page( 254 | Extension(ctx): Extension, 255 | headers: HeaderMap, 256 | ) -> Result, String> { 257 | let domain = get_domain_from_referrer(&headers); 258 | if domain.is_ok() { 259 | let _ = ctx 260 | .boring_visitor( 261 | Some(crate::app_model::VisitorType::Referer), 262 | &domain.unwrap(), 263 | &headers, 264 | ) 265 | .await; 266 | } 267 | 268 | let rank = ctx.rank.read().await.to_owned(); 269 | 270 | let mut rank_and_membership_to_be_remove = Vec::new(); 271 | 272 | let mut rank_and_membership = Vec::new(); 273 | 274 | rank.iter() 275 | .filter(|r| ctx.id2member.contains_key(&r.membership_id)) 276 | .for_each(|r| { 277 | if r.updated_at > now_shanghai() - chrono::Duration::days(30) { 278 | let m = ctx.id2member.get(&r.membership_id).unwrap().to_owned(); 279 | rank_and_membership.push(RankAndMembership { 280 | rank: r.to_owned(), 281 | membership: m, 282 | }); 283 | } else { 284 | let m = ctx.id2member.get(&r.membership_id).unwrap().to_owned(); 285 | rank_and_membership_to_be_remove.push(RankAndMembership { 286 | rank: r.to_owned(), 287 | membership: m, 288 | }); 289 | } 290 | }); 291 | 292 | let tpl = RankTemplate { 293 | rank: rank_and_membership, 294 | to_be_remove: rank_and_membership_to_be_remove, 295 | version: GIT_HASH[0..8].to_string(), 296 | }; 297 | let html = tpl.render().map_err(|err| err.to_string())?; 298 | Ok(Html(html)) 299 | } 300 | 301 | fn get_domain_from_referrer(headers: &HeaderMap) -> Result { 302 | let referrer_header = headers.get("Referer"); 303 | if referrer_header.is_none() { 304 | return Err(anyhow!("no referrer header")); 305 | } 306 | 307 | let referrer_str = String::from_utf8(referrer_header.unwrap().as_bytes().to_vec()); 308 | if referrer_str.is_err() { 309 | return Err(anyhow!("referrer header is not valid utf-8 string")); 310 | } 311 | 312 | let referrer_url = url::Url::parse(&referrer_str.unwrap()); 313 | if referrer_url.is_err() { 314 | return Err(anyhow!("referrer header is not valid URL")); 315 | } 316 | 317 | let referrer_url = referrer_url.unwrap(); 318 | if referrer_url.domain().is_none() { 319 | return Err(anyhow!("referrer header doesn't contains a valid domain")); 320 | } 321 | 322 | return Ok(referrer_url.domain().unwrap().to_string()); 323 | } 324 | 325 | async fn render_svg(tend: (&str, i64, i64, i64), render: &BoringFace) -> Response { 326 | let headers = Headers([("content-type", "image/svg+xml")]); 327 | ( 328 | StatusCode::OK, 329 | headers, 330 | render.render_svg(tend.0, tend.1, tend.2, tend.3), 331 | ) 332 | .into_response() 333 | } 334 | -------------------------------------------------------------------------------- /src/app_model.rs: -------------------------------------------------------------------------------- 1 | use std::fs; 2 | use std::time::Duration; 3 | use std::{collections::HashMap, sync::Arc}; 4 | 5 | use crate::statistics_model::Statistics; 6 | use crate::{boring_face::BoringFace, DbPool}; 7 | use crate::{now_shanghai, SYSTEM_DOMAIN}; 8 | 9 | use crate::membership_model::Membership; 10 | use anyhow::anyhow; 11 | use chrono::{NaiveDateTime, NaiveTime}; 12 | use headers::HeaderMap; 13 | use lazy_static::lazy_static; 14 | use regex::Regex; 15 | use serde::Serialize; 16 | use serde_repr::*; 17 | use tokio::sync::watch::{self, Receiver, Sender}; 18 | use tokio::sync::RwLock; 19 | use tracing::info; 20 | 21 | pub type DynContext = Arc; 22 | 23 | lazy_static! { 24 | static ref IPV4_MASK: Regex = Regex::new("(\\d*\\.).*(\\.\\d*)").unwrap(); 25 | static ref IPV6_MASK: Regex = Regex::new("(\\w*:\\w*:).*(:\\w*:\\w*)").unwrap(); 26 | } 27 | 28 | #[derive(Serialize)] 29 | struct VistEvent { 30 | ip: String, 31 | country: String, 32 | member: Membership, 33 | vt: Option, 34 | } 35 | 36 | #[derive(Serialize_repr, Debug, PartialEq, Clone, Copy)] 37 | #[repr(u8)] 38 | pub enum VisitorType { 39 | Referer = 1, 40 | Badge = 2, 41 | ICON = 3, 42 | } 43 | 44 | pub struct Context { 45 | pub badge: BoringFace, 46 | pub favicon: BoringFace, 47 | pub icon: BoringFace, 48 | 49 | pub db_pool: DbPool, 50 | pub unique_visitor: RwLock>, 51 | pub referrer: RwLock>, 52 | pub rank_svg: RwLock, 53 | 54 | pub domain2id: HashMap, 55 | pub id2member: HashMap, 56 | 57 | pub visitor_tx: Sender, 58 | pub visitor_rx: Receiver, 59 | 60 | pub rank: RwLock>, 61 | pub monthly_rank: RwLock>, 62 | 63 | pub cache: r_cache::cache::Cache, 64 | } 65 | 66 | impl Context { 67 | pub async fn get_tend_from_uv_and_rv(&self, uv: i64, rv: i64) -> i64 { 68 | let tend = (uv + rv) / self.rank_svg.read().await.to_owned(); 69 | if tend > 10 { 70 | return 10; 71 | } else if tend < 1 { 72 | return 1; 73 | } 74 | tend 75 | } 76 | 77 | pub async fn boring_visitor( 78 | &self, 79 | v_type: Option, 80 | domain: &str, 81 | headers: &HeaderMap, 82 | ) -> Result<(&str, i64, i64, i64), anyhow::Error> { 83 | if v_type.is_some_and(|v| v == VisitorType::Referer) && domain.eq(&*SYSTEM_DOMAIN) { 84 | return Err(anyhow!("system domain")); 85 | } 86 | if let Some(id) = self.domain2id.get(domain) { 87 | let ip = 88 | String::from_utf8(headers.get("CF-Connecting-IP").unwrap().as_bytes().to_vec()) 89 | .unwrap(); 90 | info!("ip {}", ip); 91 | 92 | let country = 93 | String::from_utf8(headers.get("CF-IPCountry").unwrap().as_bytes().to_vec()) 94 | .unwrap(); 95 | info!("country {}", country); 96 | 97 | let visitor_key = format!("{}_{}_{:?}", ip, id, v_type); 98 | let visitor_cache = self.cache.get(&visitor_key).await; 99 | 100 | if v_type.is_some_and(|v| [VisitorType::Referer, VisitorType::Badge].contains(&v)) 101 | && visitor_cache.is_none() 102 | { 103 | self.cache 104 | .set(visitor_key, (), Some(Duration::from_secs(60 * 60 * 4))) 105 | .await; 106 | } 107 | 108 | let mut notification = false; 109 | 110 | let mut referrer = self.referrer.write().await; 111 | let mut dist_r = referrer 112 | .get(id) 113 | .unwrap_or(&(0, NaiveDateTime::from_timestamp(0, 0))) 114 | .to_owned(); 115 | if v_type.is_some_and(|v| v == VisitorType::Referer) { 116 | if visitor_cache.is_none() { 117 | dist_r.0 += 1; 118 | dist_r.1 = now_shanghai(); 119 | referrer.insert(*id, dist_r); 120 | } 121 | notification = true; 122 | } 123 | drop(referrer); 124 | 125 | let mut uv = self.unique_visitor.write().await; 126 | let mut dist_uv = uv 127 | .get(id) 128 | .unwrap_or(&(0, NaiveDateTime::from_timestamp(0, 0))) 129 | .to_owned(); 130 | if v_type.is_some_and(|v| v == VisitorType::Badge) { 131 | if visitor_cache.is_none() { 132 | dist_uv.0 += 1; 133 | dist_uv.1 = now_shanghai(); 134 | uv.insert(*id, dist_uv); 135 | } 136 | notification = true; 137 | } 138 | drop(uv); 139 | 140 | let tend = self.get_tend_from_uv_and_rv(dist_uv.0, dist_r.0).await; 141 | 142 | if notification { 143 | let mut member = self.id2member.get(id).unwrap().to_owned(); 144 | member.description = "".to_string(); 145 | member.icon = "".to_string(); 146 | member.github_username = "".to_string(); 147 | 148 | let _ = self.visitor_tx.send( 149 | serde_json::json!(VistEvent { 150 | ip: IPV6_MASK 151 | .replace_all(&IPV4_MASK.replace_all(&ip, "$1****$2"), "$1****$2") 152 | .to_string(), 153 | country, 154 | member, 155 | vt: v_type, 156 | }) 157 | .to_string(), 158 | ); 159 | } 160 | 161 | return Ok(( 162 | &self.id2member.get(id).unwrap().name, 163 | dist_uv.0, 164 | dist_r.0, 165 | tend, 166 | )); 167 | } 168 | Err(anyhow!("not a member")) 169 | } 170 | 171 | pub async fn default(db_pool: DbPool) -> Context { 172 | let statistics = Statistics::today(db_pool.get().unwrap()).unwrap_or_default(); 173 | 174 | let mut page_view: HashMap = HashMap::new(); 175 | let mut referrer: HashMap = HashMap::new(); 176 | 177 | statistics.iter().for_each(|s| { 178 | page_view.insert(s.membership_id, (s.unique_visitor, s.updated_at)); 179 | referrer.insert(s.membership_id, (s.referrer, s.latest_referrer_at)); 180 | }); 181 | 182 | let mut membership: HashMap = 183 | serde_json::from_str(&fs::read_to_string("./resources/membership.json").unwrap()) 184 | .unwrap(); 185 | membership.retain(|_, v| v.hidden.is_none() || !v.hidden.unwrap()); 186 | 187 | let mut domain2id: HashMap = HashMap::new(); 188 | membership.iter_mut().for_each(|(k, v)| { 189 | v.id = *k; // 将 ID 补给 member 190 | domain2id.insert(v.domain.clone(), *k); 191 | }); 192 | 193 | let rank = Statistics::rank_between( 194 | db_pool.get().unwrap(), 195 | NaiveDateTime::from_timestamp(0, 0), 196 | now_shanghai(), 197 | ) 198 | .unwrap(); 199 | 200 | let monthly_rank = Statistics::rank_between( 201 | db_pool.get().unwrap(), 202 | now_shanghai() - chrono::Duration::days(30), 203 | now_shanghai(), 204 | ) 205 | .unwrap(); 206 | 207 | let (visitor_tx, visitor_rx) = watch::channel::("".to_string()); 208 | 209 | let rank_svg = Statistics::prev_day_rank_avg(db_pool.get().unwrap()); 210 | 211 | Context { 212 | badge: BoringFace::new("#d0273e".to_string(), "#f5acb9".to_string(), true), 213 | favicon: BoringFace::new("#f5acb9".to_string(), "#d0273e".to_string(), false), 214 | icon: BoringFace::new("#d0273e".to_string(), "#f5acb9".to_string(), false), 215 | db_pool, 216 | 217 | unique_visitor: RwLock::new(page_view), 218 | referrer: RwLock::new(referrer), 219 | rank_svg: RwLock::new(rank_svg), 220 | rank: RwLock::new(rank), 221 | monthly_rank: RwLock::new(monthly_rank), 222 | 223 | domain2id, 224 | id2member: membership, 225 | 226 | visitor_rx, 227 | visitor_tx, 228 | 229 | cache: r_cache::cache::Cache::new(Some(Duration::from_secs(60 * 10))), 230 | } 231 | } 232 | 233 | // 每五分钟存一次,发现隔天刷新 234 | pub async fn save_per_5_minutes(&self) { 235 | let mut uv_cache: HashMap = HashMap::new(); 236 | let mut referrer_cache: HashMap = HashMap::new(); 237 | let mut changed_list: Vec = Vec::new(); 238 | let mut _today = NaiveDateTime::new(now_shanghai().date(), NaiveTime::from_hms(0, 0, 0)); 239 | let id_list = Vec::from_iter(self.id2member.keys()); 240 | loop { 241 | tokio::time::sleep(Duration::from_secs(60 * 5)).await; 242 | changed_list.clear(); 243 | // 对比是否有数据更新 244 | let mut uv_write = self.unique_visitor.write().await; 245 | let mut referrer_write = self.referrer.write().await; 246 | id_list.iter().for_each(|id| { 247 | let uv = *uv_cache 248 | .get(id) 249 | .unwrap_or(&(0, NaiveDateTime::from_timestamp(0, 0))); 250 | let new_uv = *uv_write 251 | .get(id) 252 | .unwrap_or(&(0, NaiveDateTime::from_timestamp(0, 0))); 253 | if uv.0.ne(&new_uv.0) { 254 | uv_cache.insert(**id, new_uv); 255 | changed_list.push(**id); 256 | } 257 | let referrer = *referrer_cache 258 | .get(id) 259 | .unwrap_or(&(0, NaiveDateTime::from_timestamp(0, 0))); 260 | let new_referrer = *referrer_write 261 | .get(id) 262 | .unwrap_or(&(0, NaiveDateTime::from_timestamp(0, 0))); 263 | if referrer.0.ne(&new_referrer.0) { 264 | referrer_cache.insert(**id, new_referrer); 265 | if !changed_list.contains(id) { 266 | changed_list.push(**id); 267 | } 268 | } 269 | }); 270 | // 更新到数据库 271 | changed_list.iter().for_each(|id| { 272 | let id_uv = *uv_cache 273 | .get(id) 274 | .unwrap_or(&(0, NaiveDateTime::from_timestamp(0, 0))); 275 | let id_referrer = *referrer_cache 276 | .get(id) 277 | .unwrap_or(&(0, NaiveDateTime::from_timestamp(0, 0))); 278 | Statistics::insert_or_update( 279 | self.db_pool.get().unwrap(), 280 | &Statistics { 281 | created_at: _today, 282 | membership_id: *id, 283 | unique_visitor: id_uv.0, 284 | updated_at: id_uv.1, 285 | referrer: id_referrer.0, 286 | latest_referrer_at: id_referrer.1, 287 | id: 0, 288 | }, 289 | ) 290 | .unwrap(); 291 | }); 292 | let new_day = NaiveDateTime::new(now_shanghai().date(), NaiveTime::from_hms(0, 0, 0)); 293 | if new_day.ne(&_today) { 294 | _today = new_day; 295 | // 如果是跨天重置数据 296 | uv_write.clear(); 297 | referrer_write.clear(); 298 | uv_cache.clear(); 299 | referrer_cache.clear(); 300 | // 重置访问打点 301 | self.cache.clear().await; 302 | // 更新上日访问量均值 303 | let mut rank_svg = self.rank_svg.write().await; 304 | *rank_svg = Statistics::prev_day_rank_avg(self.db_pool.get().unwrap()); 305 | } 306 | drop(uv_write); 307 | drop(referrer_write); 308 | 309 | let mut rank = self.rank.write().await; 310 | *rank = Statistics::rank_between( 311 | self.db_pool.get().unwrap(), 312 | NaiveDateTime::from_timestamp(0, 0), 313 | now_shanghai(), 314 | ) 315 | .unwrap(); 316 | 317 | let mut monthly_rank = self.monthly_rank.write().await; 318 | *monthly_rank = Statistics::rank_between( 319 | self.db_pool.get().unwrap(), 320 | now_shanghai() - chrono::Duration::days(30), 321 | now_shanghai(), 322 | ) 323 | .unwrap(); 324 | } 325 | } 326 | } 327 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 无聊湾 2 | 3 | NAiVe · 无聊人的中继站。图标修改自熊大的 4 | 5 | ## 加入我们 6 | 7 | 提交 PR 将你的网站添加进 `resources/membership.json` 8 | 9 | naiba 10 | Kris 11 | Frederick Chen 12 | 王九弦SZ·Ninty 13 | JB20CM 14 | Mello 15 | MCTGyt 16 | WoLeo-Z 17 | 懋和道人 18 | Kokona_Shiki 19 | jipa233 20 | gitmba 21 | Kyomu 22 | ShSuperyun 23 | 熊宝 24 | 枫韵 25 | 圆周率3014 26 | yunze 27 | XCATX 28 | The hanta 29 | SaboZhang 30 | Qindarkstone 31 | Mauzhu 32 | Handsome 33 | Fuever 34 | Caesar 35 | 澪度 36 | lololololowe 37 | longchunxin 38 | lyle 39 | miniwater 40 | nieshilin 41 | qiyao 42 | sdfghjwmbkdouble 43 | 黑歌 44 | kufx 45 | it985 46 | imshadow 47 | Kris 48 | guangzhoueven 49 | gogei2010 50 | dreamerhe114514 51 | citihu 52 | btwoa 53 | langpa 54 | 缘生 55 | 王兴家 56 | MortalCat 57 | 星然♚ 58 | wumingblog 59 | RyuChanฅ 60 | 小尘 Xiao Chen 61 | yinzhuoqun 62 | Qiankun Xia 63 | xinye 64 | 嘿哟 65 | wzwzx 66 | Masone 67 | tianhukj 68 | tech-fever 69 | spiritlhl 70 | bbb-lsy07 71 | LixdHappy 72 | Percival Zheng 73 | LBB 74 | Joker 75 | Jochen233 76 | JianHao Yang 77 | Howie Xie 78 | HongShi211 79 | HansJack 80 | Gil Schneider 81 | Echo846 82 | Calyee 83 | Calvert Lee 84 | Amitabha 85 | AdeleNaumann 86 | 9527DHX 87 | 2022471674/28.7 88 | awaae 89 | Zlemoni 90 | Yicheng Ding 91 | Watermelonabc 92 | Veitzn 93 | Supermini233 94 | Styunlen 95 | SeerSu 96 | zheep 97 | SEYYl 98 | Redcha 99 | KuriCL 100 | Rabbit_xuan 101 | Pstarchen 102 | Wujingbo 103 | OvO 104 | mcenahle 105 | 106 | ## Migration 107 | 108 | ```sh 109 | docker-compose run --rm naive /webapp/naive migration 110 | ``` 111 | -------------------------------------------------------------------------------- /resources/membership.json: -------------------------------------------------------------------------------- 1 | { 2 | "1": { 3 | "domain": "lifelonglearn.ing", 4 | "icon": "https://lifelonglearn.ing/static/cactus/images/logo.png", 5 | "name": "终身学习", 6 | "description": "奶爸博客", 7 | "github_username": "naiba/solitudes" 8 | }, 9 | "2": { 10 | "domain": "boringbay.com", 11 | "icon": "/api/icon/boringbay.com", 12 | "name": "The Boring Bay", 13 | "description": "无聊的人那么多~", 14 | "github_username": "cantoblanco/boringbay" 15 | }, 16 | "3": { 17 | "domain": "www.spiritysdx.top", 18 | "icon": "https://ftp.bmp.ovh/imgs/2021/07/7f4988ae3b43e55d.jpg", 19 | "name": "二叉树的博客", 20 | "description": "二叉树上的我", 21 | "github_username": "spiritlhls" 22 | }, 23 | "5": { 24 | "domain": "ops.naibahq.com", 25 | "icon": "https://ops.naibahq.com/static/logo.svg", 26 | "name": "哪吒监控", 27 | "description": "没有比这个更好玩的了", 28 | "github_username": "naiba/nezha" 29 | }, 30 | "6": { 31 | "domain": "www.ilay.top", 32 | "icon": "https://cdn.jsdelivr.net/gh/ilay1678/cdn@master/favicon.png", 33 | "name": "我若为王", 34 | "description": "Just do it!", 35 | "github_username": "ilay1678", 36 | "hidden": true 37 | }, 38 | "9": { 39 | "domain": "www.ifts.ml", 40 | "icon": "https://cdn.jsdelivr.net/gh/DaoChen6/img@main/logo1.png", 41 | "name": "辰漉博客", 42 | "description": "道辰的博客", 43 | "github_username": "DaoChen6", 44 | "hidden": true 45 | }, 46 | "10": { 47 | "domain": "www.gitmba.com", 48 | "icon": "https://vkceyugu.cdn.bspapp.com/VKCEYUGU-ec093c08-f8d6-4ae7-ae84-bbd115c10996/dfe39fcf-3e7a-463d-89b9-42b15563fb95.png", 49 | "name": "GiTMBA", 50 | "description": "探索新知识的宝藏小站", 51 | "github_username": "qmxs", 52 | "hidden": true 53 | }, 54 | "12": { 55 | "domain": "cymj.ltd", 56 | "icon": "https://www.imageoss.com/images/2022/10/19/64f03244415031698418cd627f638651.jpg", 57 | "name": "轻梦", 58 | "description": "日常或加入一个小家庭", 59 | "github_username": "xuanxuan666niupi666", 60 | "hidden": true 61 | }, 62 | "13": { 63 | "domain": "mctg.ink", 64 | "icon": "http://us-host.nets.hk/application/thumb.php?img=/i/2022/02/28/pga7dl.jpg", 65 | "name": "天哥de博客", 66 | "description": "helloworld", 67 | "github_username": "MCTGyt", 68 | "hidden": true 69 | }, 70 | "15": { 71 | "domain": "www.dao.js.cn", 72 | "icon": "https://www.dao.js.cn/zb_users/upload/2021/10/202110151634246697912592.png", 73 | "name": "懋和道人", 74 | "description": "李懋和,俗名李栋梁。书法、国画爱好者,互联网安全与前端建设者。", 75 | "github_username": "wulintang" 76 | }, 77 | "18": { 78 | "domain": "blog.wol1.xyz", 79 | "icon": "https://blog.wol1.xyz/favicon.ico", 80 | "name": "WoL's Blog", 81 | "description": "Powered by WoL", 82 | "github_username": "WoLeo-Z", 83 | "hidden": true 84 | }, 85 | "19": { 86 | "domain": "sun.cm", 87 | "icon": "https://telegraph-image-e2j.pages.dev/file/aace1ce621c93938d3dfb.png", 88 | "name": "白歌", 89 | "description": "Just for funnnnn!!", 90 | "github_username": "cantoblanco" 91 | }, 92 | "20": { 93 | "domain": "pawswrite.xyz", 94 | "icon": "https://pawswrite.xyz/images/avatar.gif", 95 | "name": "fever", 96 | "description": "好想发财", 97 | "github_username": "tech-fever", 98 | "hidden": true 99 | }, 100 | "21": { 101 | "domain": "maolog.com", 102 | "icon": "https://maolog.com/logo", 103 | "name": "茂茂の部落格", 104 | "description": "随笔记录", 105 | "github_username": "maolog", 106 | "hidden": true 107 | }, 108 | "22": { 109 | "domain": "www.yuanzj.top", 110 | "icon": "https://www.yuanzj.top/img/favicon.jpg", 111 | "name": "圆周率的博客", 112 | "description": "My Blog!", 113 | "github_username": "yzl3014" 114 | }, 115 | "24": { 116 | "domain": "www.rua.ink", 117 | "icon": "https://imglogo.rua.ink/logo.webp", 118 | "name": "Mello", 119 | "description": "在这里写写画画", 120 | "github_username": "muyan0709" 121 | }, 122 | "28": { 123 | "domain": "www.kuhehe.top", 124 | "icon": "https://bu.dusays.com/2024/03/19/65f9afc7064fd.jpg", 125 | "name": "酷小呵", 126 | "description": "爱分享的酷小呵", 127 | "github_username": "Kufx" 128 | }, 129 | "29": { 130 | "domain": "www.0du.top", 131 | "icon": "https://www.0du.top/favicon.ico", 132 | "name": "澪度の博客", 133 | "description": "可能会随便写点什么", 134 | "github_username": "lingdu2333", 135 | "hidden": true 136 | }, 137 | "30": { 138 | "domain": "solitud.es", 139 | "icon": "https://s3.solitud.es/solitude/photo_2023-04-20_22-16-39.jpg", 140 | "name": "The Lonely House", 141 | "description": "Solitude and Reflection", 142 | "github_username": "dysf888" 143 | }, 144 | "31": { 145 | "domain": "bigtoyscompany.com", 146 | "icon": "https://bigtoyscompany.com/assets/logo.e75135c1.png", 147 | "name": "大玩具公司", 148 | "description": "Big toys for big boys.", 149 | "github_username": "naiba", 150 | "hidden": true 151 | }, 152 | "32": { 153 | "domain": "blog.qcmoe.com", 154 | "icon": "https://blog.qcmoe.com/content/uploadfile/tpl_options-master/favicon.jpg", 155 | "name": "倾丞の小窝", 156 | "description": "倾丞的小窝,❤心随我意,行路千里。.", 157 | "github_username": "Jochen233" 158 | }, 159 | "33": { 160 | "domain": "bokebo.com", 161 | "icon": "https://bokebo.com/logo.png", 162 | "name": "BOKEBO", 163 | "description": "BO的博客。", 164 | "github_username": "xiaoxing07", 165 | "hidden": true 166 | }, 167 | "34": { 168 | "domain": "imjipa.top", 169 | "icon": "https://imjipa.top/static/avatar.jpg", 170 | "name": "JIPA233の小窝", 171 | "description": "Deed divides beings into lower and higher ones.", 172 | "github_username": "jipa233" 173 | }, 174 | "35": { 175 | "domain": "blog.xiowo.net", 176 | "icon": "https://blog.xiowo.net/img/avatar.png", 177 | "name": "Mo的记事簿", 178 | "description": "万年鸽王,哈哈OvO", 179 | "github_username": "xiowo" 180 | }, 181 | "36": { 182 | "domain": "imum.me", 183 | "icon": "https://cdn.jsdelivr.net/gh/tosspi/img@main//img/zhaoico2.png", 184 | "name": "你是人间的四月天", 185 | "description": "--你是爱,是暖,是希望,你是人间的四月天!", 186 | "github_username": "TossPi" 187 | }, 188 | "37": { 189 | "domain": "www.xxyxxvii.com", 190 | "icon": "https://www.xxyxxvii.com/Quick_links_Vehicles.png", 191 | "name": "阿衣妞妞 | 海玛塔莎洛", 192 | "description": "xxYxxVii.COM 收集并分享不同的信息", 193 | "github_username": "xxyxxvii", 194 | "hidden": true 195 | }, 196 | "39": { 197 | "domain": "v.hi.sy", 198 | "icon": "https://telegraph-image-e2j.pages.dev/file/aace1ce621c93938d3dfb.png", 199 | "name": "白歌-V", 200 | "description": "Just for funnnnn!!", 201 | "github_username": "cantoblanco", 202 | "hidden": true 203 | }, 204 | "40": { 205 | "domain": "wuminboke.site", 206 | "icon": "https://bu.dusays.com/2024/07/23/669f7b016ad0b.jpg", 207 | "name": "无名博客", 208 | "description": "一个无聊的人罢了", 209 | "github_username": "wumingblog" 210 | }, 211 | "41": { 212 | "domain": "blog.storical.fun", 213 | "icon": "https://blog.storical.fun/images/icon.png", 214 | "name": "绘星里", 215 | "description": "一起来绘制属于自己的星星!", 216 | "github_username": "SinzMise" 217 | }, 218 | "42": { 219 | "domain": "www.5had0w.com", 220 | "icon": "https://www.5had0w.com/assets/logo.png", 221 | "name": "5had0w", 222 | "description": "一个游走在互联网边缘的影子,希望在这个世界留下一点痕迹。", 223 | "github_username": "imshadow" 224 | }, 225 | "43": { 226 | "domain": "ysicing.me", 227 | "icon": "https://ysicing.me/static/cactus/images/logo.png", 228 | "name": "ysicing", 229 | "description": "新时代云缘生农民工", 230 | "github_username": "ysicing" 231 | }, 232 | "45": { 233 | "domain": "casear.net", 234 | "icon": "https://casear.net/static/img/eae10f098ce1f671fa0e60d899bf545e.avatar.svg", 235 | "name": "Casear的技术栈", 236 | "description": "如果你有机会到好望角,请在海边的石头上刻下我的故事👨‍💻", 237 | "github_username": "CasearF" 238 | }, 239 | "47": { 240 | "domain": "xieboke.net", 241 | "icon": "http://pic.xieboke.net/joyoblog.png", 242 | "name": "卓越笔记", 243 | "description": "成长的时候,能帮有需要的你。", 244 | "github_username": "yinzhuoqun" 245 | }, 246 | "48": { 247 | "domain": "9527dhx.top", 248 | "icon": "https://jsd.cdn.zzko.cn/gh/9527DHX/pic@main/pic/9527.7a318nbht2k0.png", 249 | "name": "9527DHX's Blog", 250 | "description": "Always Belive That.", 251 | "github_username": "9527DHX" 252 | }, 253 | "49": { 254 | "domain": "chuishen.xyz", 255 | "icon": "https://chuishen.xyz/img/logo.png", 256 | "name": "Lafcadia's Blog", 257 | "description": "Et in Arcadia, ego.", 258 | "github_username": "Lafcadia" 259 | }, 260 | "50": { 261 | "domain": "blog.awaae001.top", 262 | "icon": "https://blog.awaae001.top/ico.webp", 263 | "name": "呓语梦轩", 264 | "description": "用心感受生活", 265 | "github_username": "awaae001" 266 | }, 267 | "51": { 268 | "domain": "www.efu.me", 269 | "icon": "https://s3.qjqq.cn/47/660a7ece07609.webp!color", 270 | "name": "Efu", 271 | "description": "小心翼翼地拥有并努力着。", 272 | "github_username": "efuo", 273 | "hidden": true 274 | }, 275 | "52": { 276 | "domain": "blog.qjqq.cn", 277 | "icon": "https://avatar.qjqq.cn/1/6503bb1b7fa1a.webp!avatar", 278 | "name": "青桔气球", 279 | "description": "春风若有怜花意 可否许我再少年", 280 | "github_username": "PearsSauce" 281 | }, 282 | "53": { 283 | "domain": "blog.btwoa.com", 284 | "icon": "https://blog.btwoa.com/btwoa.jpg", 285 | "name": "btwoa", 286 | "description": "世界为你简单", 287 | "github_username": "btwoa", 288 | "hidden": true 289 | }, 290 | "54": { 291 | "domain": "www.heiu.top", 292 | "icon": "https://www.heiu.top/images/favicon.ico", 293 | "name": "heiu", 294 | "description": "可可爱爱", 295 | "github_username": "xiaoheiyo" 296 | }, 297 | "55": { 298 | "domain": "blog.luckyits.com", 299 | "icon": "https://blog.luckyits.com/favicon.ico", 300 | "name": "西柚", 301 | "description": "我想要两颗西柚 (i want to see you)", 302 | "github_username": "SaboZhang" 303 | }, 304 | "56": { 305 | "domain": "www.fly6022.fun", 306 | "icon": "https://www.fly6022.fun/assets/images/avatar.jpg", 307 | "name": "fly6022", 308 | "description": "满载一船星辉,在星辉斑斓里放歌。", 309 | "github_username": "fly6022", 310 | "hidden": true 311 | }, 312 | "57": { 313 | "domain": "hexo.dreamerhe.cn", 314 | "icon": "https://hexo.dreamerhe.cn/img/logo/apple-touch-icon.pngg", 315 | "name": "何星梦", 316 | "description": "朝着梦想前进,纵有万般阻挠,也绝不会让我停下脚步,待到成功时,之喜唯有我独享!", 317 | "github_username": "dreamerhe114514", 318 | "hidden": true 319 | }, 320 | "58": { 321 | "domain": "blog.yunzeo.cn", 322 | "icon": "https://www.yunzeo.cn/img/favicon.png", 323 | "name": "云泽记", 324 | "description": "热爱可抵岁月漫长!", 325 | "github_username": "yunzeo", 326 | "hidden": true 327 | }, 328 | "59": { 329 | "domain": "blog.ohdragonboi.cn", 330 | "icon": "https://blog.ohdragonboi.cn/upload/avatar.png", 331 | "name": "Frederick's Blog", 332 | "description": "Sky is the limit.", 333 | "github_username": "fenychn0206" 334 | }, 335 | "60": { 336 | "domain": "987654321.org", 337 | "icon": "https://987654321.org/img/tx.png", 338 | "name": "LCX", 339 | "description": "这是一个博客,同时也是一个知识库。", 340 | "github_username": "longchunxin" 341 | }, 342 | "61": { 343 | "domain": "blog.calyee.top", 344 | "icon": "https://blog.calyee.top/favicon.svg", 345 | "name": "CALYEE", 346 | "description": "追求充实,分享快乐。", 347 | "github_username": "yikoutian1" 348 | }, 349 | "62": { 350 | "domain": "kyomu.me", 351 | "icon": "https://kyomu.me/pfp.webp", 352 | "name": "Kyomu's Blog", 353 | "description": "What is mind? No matter. What is matter? Never mind.", 354 | "github_username": "KyomuDesu", 355 | "hidden": true 356 | }, 357 | "63": { 358 | "domain": "lik.cc", 359 | "icon": "https://www.lik.cc/upload/logo.png", 360 | "name": "Handsome", 361 | "description": "心若有所向往,何惧道阻且长", 362 | "github_username": "acanyo", 363 | "hidden": true 364 | }, 365 | "64": { 366 | "domain": "kokona.top", 367 | "icon": "https://kokona.top/wp-content/uploads/2023/05/kokona_avatar-300x300.png", 368 | "name": "Kokona Blog", 369 | "description": "发现有趣项目,分享实践经历", 370 | "github_username": "kokona-shiki" 371 | }, 372 | "65": { 373 | "domain": "blog.veitzn.top", 374 | "icon": "https://img.veitzn.top/img/VeiLogoN.webp", 375 | "name": "Vei Blog", 376 | "description": "松窗竹户,万千潇洒", 377 | "github_username": "veitzn1" 378 | }, 379 | "66": { 380 | "domain": "www.icl.site", 381 | "icon": "https://static.zhuke.com/xqk/mweb/icllogo.png", 382 | "name": "谋生者 I Create Life", 383 | "description": "一个居住在苏州的牛马,分享生活经验和见闻", 384 | "github_username": "xqk" 385 | }, 386 | "67": { 387 | "domain": "blog.mcenahle.cn", 388 | "icon": "https://blog.mcenahle.cn/upload/blog.png", 389 | "name": "梅之夏", 390 | "description": "Explore. Dream. Discover.", 391 | "github_username": "Mcenahle", 392 | "hidden": true 393 | }, 394 | "68": { 395 | "domain": "suus.me", 396 | "icon": "https://suus.me/avatar.webp", 397 | "name": "蘇 SU", 398 | "description": "在数字的海洋中,寻找属于自己的星辰。", 399 | "github_username": "suuseer", 400 | "hidden": true 401 | }, 402 | "69": { 403 | "domain": "moecx.com", 404 | "icon": "https://q1.qlogo.cn/g?b=qq&nk=1131596911&s=640", 405 | "name": "VxiN's Blog", 406 | "description": "未寄出的信,未命名的诗", 407 | "github_username": "chenxin-source", 408 | "hidden": true 409 | }, 410 | "70": { 411 | "domain": "blog.hanta2011.top", 412 | "icon": "https://pic.imgdb.cn/item/65fe67989f345e8d03923ea6.jpg", 413 | "name": "可执行程序の天境小屋🥝", 414 | "description": "The boy who lived", 415 | "github_username": "exef-star" 416 | }, 417 | "71": { 418 | "domain": "blog.245179.xyz", 419 | "icon": "https://blog.245179.xyz/images/atiq.png", 420 | "name": "龙星划空", 421 | "description": "人生近看是悲剧,远看是喜剧", 422 | "github_username": "Catwb" 423 | }, 424 | "72": { 425 | "domain": "peter267.github.io", 426 | "icon": "https://wmimg.com/i/1169/2024/09/66ec1739c38a0.png", 427 | "name": "Peter267", 428 | "description": "无限进步!", 429 | "github_username": "Peter267" 430 | }, 431 | "73": { 432 | "domain": "watermelonabc.top", 433 | "icon": "https://watermelonabc.top/asset/icon/Avatar.png", 434 | "name": "Watermelonabc", 435 | "description": "Re苦手的小博客", 436 | "github_username": "rt265" 437 | }, 438 | "74": { 439 | "domain": "blog.moodlog.cn", 440 | "icon": "https://img.moodlog.cn/i/2025/04/23/logo.webp", 441 | "name": "Moodlog", 442 | "description": "繁华已尽,空散云烟。", 443 | "github_username": "zlemoni" 444 | }, 445 | "75": { 446 | "domain": "blog.starchen.top", 447 | "icon": "https://blog.starchen.top/favicon.jpg", 448 | "name": "星辰博客", 449 | "description": "以热爱之名,点亮技术探索者的漫长岁月!", 450 | "github_username": "Pstarchen" 451 | }, 452 | "76": { 453 | "domain": "howiehz.top", 454 | "icon": "https://howiehz.top/upload/ico.ico", 455 | "name": "皓子的小站", 456 | "description": "互联网是一片海洋,网站犹如一座座孤岛漂浮在其上,唯有超链接将它们联系起来。而此处恰好就是一座小岛,欢迎访问皓子的小站。", 457 | "github_username": "HowieHz" 458 | }, 459 | "77": { 460 | "domain": "styunlen.cn", 461 | "icon": "https://styunlen.cn/hello.png", 462 | "name": "九仞之行", 463 | "description": "严于律己,宽以待人,深自警省,讷言敏行", 464 | "github_username": "Styunlen" 465 | }, 466 | "78": { 467 | "domain": "www.zheep.top", 468 | "icon": "https://www.zheep.top/img/uuz.jpg", 469 | "name": "云端的解构者", 470 | "description": "以我观物,故物皆著我之色彩", 471 | "github_username": "zheep1209" 472 | }, 473 | "79": { 474 | "domain": "blog.study996.cn", 475 | "icon": "https://blog.study996.cn/upload/favicon.ico", 476 | "name": "寻梦城", 477 | "description": "欲买桂花同载酒 终不似,少年游", 478 | "github_username": "it985", 479 | "hidden": true 480 | }, 481 | "80":{ 482 | "domain": "xingwangzhe.fun", 483 | "icon": "https://xingwangzhe.fun/favicon.ico", 484 | "name": "姓王者的博客", 485 | "description": "学习,进步,创造无限可能", 486 | "github_username": "xingwangzhe" 487 | }, 488 | "81":{ 489 | "domain": "echo.imjianhao.cn", 490 | "icon": "https://echo.imjianhao.cn/images/User.webp", 491 | "name": "小杨的自留地", 492 | "description": "记录感悟,分享有趣", 493 | "github_username": "yjh2643408123" 494 | }, 495 | "82":{ 496 | "domain": "blog.gbfun.cc", 497 | "icon": "https://blog-image-1302787555.cos.ap-guangzhou.myqcloud.com//img%E7%88%86%E7%B1%B3%E8%8A%B1.svg", 498 | "name": "GB 的记事簿", 499 | "description": "热爱生活", 500 | "github_username": "LixdHappy" 501 | }, 502 | "83":{ 503 | "domain": "www.qindarkstone.cn", 504 | "icon": "https://i.postimg.cc/ryxGSQ7P/hellokitty-kitty-cat-aigei-com.png", 505 | "name": "暗石の小站", 506 | "description": "peace&love", 507 | "github_username": "Qindarkstone", 508 | "hidden": true 509 | }, 510 | "84":{ 511 | "domain": "kali.rf.gd", 512 | "icon": "http://kali.rf.gd/wp-content/uploads/2025/07/1752018321-login_logo-1.webp", 513 | "name": "Kaliの妙妙屋", 514 | "description": "边走边悟", 515 | "github_username": "tianhukj", 516 | "hidden": true 517 | }, 518 | "85":{ 519 | "domain": "gogei.netlify.app", 520 | "icon": "https://gogei.netlify.app/images/favicon.ico", 521 | "name": "Gogei的静默日志", 522 | "description": "字如萤逝,码若流光", 523 | "github_username": "gogei-cn" 524 | }, 525 | "86":{ 526 | "domain": "www.hansjack.com", 527 | "icon": "https://www.hansjack.com/favicon.ico", 528 | "name": "时光流·言", 529 | "description": "个人博客,持续分享网站部署实战经验、精选书评解读和生活观察手记。 这里提供可复用的技术教程、深度阅读指南和真实生活洞察,与技术爱好者一起进步......", 530 | "github_username": "TGU-HansJack" 531 | }, 532 | "87":{ 533 | "domain": "note.redcha.cn", 534 | "icon": "https://note.redcha.cn/favicon.ico", 535 | "name": "彬红茶日记", 536 | "description": "我的个人生活与技术生活日记", 537 | "github_username": "kuang2714" 538 | }, 539 | "88":{ 540 | "domain": "lhcy.org", 541 | "icon": "https://lhcy.org/favicon.ico", 542 | "name": "林海草原", 543 | "description": "愿我经历的苦难你不要经历,愿我已有的幸福你触手可及", 544 | "github_username": "linhaii", 545 | "hidden": true 546 | }, 547 | "89":{ 548 | "domain": "blog.lylelove.top", 549 | "icon": "https://blog.lylelove.top/media/website/yuan.png", 550 | "name": "竟何", 551 | "description": "写给你的长信", 552 | "github_username": "lylelove" 553 | }, 554 | "90": { 555 | "domain": "blog.131714.xyz", 556 | "icon": "https://img.xiaozhangya.xin/file/Avatar/avatar20.jpg", 557 | "name": "小张の小站", 558 | "description": "欲买桂花同载酒", 559 | "github_username": "xz131714" 560 | }, 561 | "91": { 562 | "domain": "blog.00000106.xyz", 563 | "icon": "https://blog.00000106.xyz/img/favicon.ico", 564 | "name": "浮日誌", 565 | "description": "本是青燈不歸客,卻因濁酒留風塵.", 566 | "github_username": "Elegy17" 567 | }, 568 | "92": { 569 | "domain": "www.elysium-stack.cn", 570 | "icon": "https://www.elysium-stack.cn/upload/ee0757e6-ebb4-46d0-a388-660f648653ea.jpg", 571 | "name": "ElysiumStack", 572 | "description": "不会摄影的设计师不是优秀的旅行家", 573 | "github_username": "ChocoboQJJ" 574 | }, 575 | "93": { 576 | "domain": "www.calvertlee.top", 577 | "icon": "http://39.99.44.163/img/Portrait.png", 578 | "name": "李欣Calvert", 579 | "description": "用心书写内心的共鸣", 580 | "github_username": "Calvert97" 581 | }, 582 | "94": { 583 | "domain": "blog.lololowe.com", 584 | "icon": "https://blog.lololowe.com/img/favicon.ico", 585 | "name": "lololowe的博客", 586 | "description": "宁静致远", 587 | "github_username": "lololowe" 588 | }, 589 | "95": { 590 | "domain": "www.qqe4.com", 591 | "icon": "https://image.qqe4.com/Piclist/20250417204903752.jpg", 592 | "name": "OvO", 593 | "description": "热爱生活,分享科技", 594 | "github_username": "xiangleovo" 595 | }, 596 | "96": { 597 | "domain": "blog.mcxiaochen.top", 598 | "icon": "https://blog.mcxiaochen.top/favicon.ico", 599 | "name": "辰渊尘的博客", 600 | "description": "有志不在年高", 601 | "github_username": "mcxiaochenn" 602 | }, 603 | "97": { 604 | "domain": "qxzhan.cn", 605 | "icon": "https://qxzhan.cn/favicon.ico", 606 | "name": "青序栈", 607 | "description": "青序成栈·向简而生", 608 | "github_username": "scfcn" 609 | }, 610 | "98": { 611 | "domain": "200536.xyz", 612 | "icon": "https://200536.xyz/icon.svg", 613 | "name": "ACCB", 614 | "description": "破烂中寻找记忆.", 615 | "github_username": "Lbb886" 616 | }, 617 | "99": { 618 | "domain": "blog.kong.college", 619 | "icon": "https://image.kong.college/i/2025/09/09/spq9n6.png", 620 | "name": "28.7的博客", 621 | "description": "空山不见人,但闻人语响", 622 | "github_username": "2022471674" 623 | }, 624 | "100": { 625 | "domain": "www.sumi233.top", 626 | "icon": "https://cdn.sumi233.top/gh/huang233893/blog-image-bed/top/huang233893/imgs/blog/userfb6a1018b84ce485.jpg", 627 | "name": "酥米的小站", 628 | "description": "终有一日,寻梦中人", 629 | "github_username": "huang233893" 630 | }, 631 | "101": { 632 | "domain": "tiger.work", 633 | "icon": "https://tiger.work/content/images/size/w256h256/2025/09/favicon.png", 634 | "name": "虎行独语", 635 | "description": "追逐感官层面各种意义上的丰满。", 636 | "github_username": "akaTiger" 637 | }, 638 | "102": { 639 | "domain": "woolyun.com", 640 | "icon": "https://woolyun.com/wp-content/uploads/2025/05/yyl-e1746698677584.jpg", 641 | "name": "天海博客", 642 | "description": "建站经验记录,实用工具推荐,免费资源分享。", 643 | "github_username": "citihu" 644 | }, 645 | "103": { 646 | "domain": "hongshi.qzz.io", 647 | "icon": "https://hongshi.qzz.io/favicon.png", 648 | "name": "红石の空间站", 649 | "description": "一个高中牲的小站。", 650 | "github_username": "HongShi211" 651 | }, 652 | "104": { 653 | "domain": "blog.6uu.us", 654 | "icon": "https://blog.6uu.us/upload/lsyb.png", 655 | "name": "bbb-lsy07", 656 | "description": "科技激荡人文,洞见智慧本真。", 657 | "github_username": "bbb-lsy07" 658 | }, 659 | "105": { 660 | "domain": "www.krjojo.com", 661 | "icon": "https://www.krjojo.com/wp-content/uploads/2025/02/毛毛虫2.avif", 662 | "name": "手里有只毛毛虫", 663 | "description": "礼印封缄寄意深,外盒承情胜万金", 664 | "github_username": "miniwater" 665 | }, 666 | "106": { 667 | "domain": "blog.guangzhoueven.dpdns.org", 668 | "icon": "https://blog.guangzhoueven.dpdns.org/img/logo.png", 669 | "name": "guangzhou_even's blog", 670 | "description": "一个平凡的人", 671 | "github_username": "guangzhoueven" 672 | }, 673 | "107": { 674 | "domain": "blog.2xgh.qzz.io", 675 | "icon": "https://vip.123pan.cn/1830248277/yk6baz03t0m000d7w33gpcmh40mzyt9jDIYwDdixBdi0AvxwDqrOBa==.jpg", 676 | "name": "LegspCpd Blog", 677 | "description": "我们还有最后一个办法!", 678 | "github_username": "Echo846" 679 | }, 680 | "108": { 681 | "domain": "xiaonie.xyz", 682 | "icon": "https://xiaonie.xyz/upload/logo.webp", 683 | "name": "小聂的窝", 684 | "description": "一个简单纯粹的个人分享空间", 685 | "github_username": "nieshilin" 686 | } 687 | } 688 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "0.7.18" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "ansi_term" 16 | version = "0.12.1" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" 19 | dependencies = [ 20 | "winapi", 21 | ] 22 | 23 | [[package]] 24 | name = "anyhow" 25 | version = "1.0.45" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "ee10e43ae4a853c0a3591d4e2ada1719e553be18199d9da9d4a83f5927c2f5c7" 28 | 29 | [[package]] 30 | name = "askama" 31 | version = "0.11.0" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "4d8f355701c672c2ba3d718acbd213f740beea577cc4eae66accdffe15be1882" 34 | dependencies = [ 35 | "askama_derive", 36 | "askama_escape", 37 | "askama_shared", 38 | ] 39 | 40 | [[package]] 41 | name = "askama_derive" 42 | version = "0.11.0" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "84704cab5b7ae0fd3a9f78ee5eb7b27f3749df445f04623db6633459ae283267" 45 | dependencies = [ 46 | "askama_shared", 47 | "proc-macro2", 48 | "syn 1.0.81", 49 | ] 50 | 51 | [[package]] 52 | name = "askama_escape" 53 | version = "0.10.2" 54 | source = "registry+https://github.com/rust-lang/crates.io-index" 55 | checksum = "9a1bb320f97e6edf9f756bf015900038e43c7700e059688e5724a928c8f3b8d5" 56 | 57 | [[package]] 58 | name = "askama_shared" 59 | version = "0.12.0" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | checksum = "dae03eebba55a2697a376e58b573a29fe36893157173ac8df312ad85f3c0e012" 62 | dependencies = [ 63 | "askama_escape", 64 | "humansize", 65 | "nom", 66 | "num-traits", 67 | "percent-encoding", 68 | "proc-macro2", 69 | "quote", 70 | "serde", 71 | "syn 1.0.81", 72 | "toml", 73 | ] 74 | 75 | [[package]] 76 | name = "async-lock" 77 | version = "2.4.0" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | checksum = "e6a8ea61bf9947a1007c5cada31e647dbc77b103c679858150003ba697ea798b" 80 | dependencies = [ 81 | "event-listener", 82 | ] 83 | 84 | [[package]] 85 | name = "async-trait" 86 | version = "0.1.51" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "44318e776df68115a881de9a8fd1b9e53368d7a4a5ce4cc48517da3393233a5e" 89 | dependencies = [ 90 | "proc-macro2", 91 | "quote", 92 | "syn 1.0.81", 93 | ] 94 | 95 | [[package]] 96 | name = "autocfg" 97 | version = "1.0.1" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 100 | 101 | [[package]] 102 | name = "axum" 103 | version = "0.4.4" 104 | source = "registry+https://github.com/rust-lang/crates.io-index" 105 | checksum = "310a147401c66e79fc78636e4db63ac68cd6acb9ece056de806ea173a15bce32" 106 | dependencies = [ 107 | "async-trait", 108 | "axum-core", 109 | "base64", 110 | "bitflags", 111 | "bytes", 112 | "futures-util", 113 | "headers", 114 | "http", 115 | "http-body", 116 | "hyper", 117 | "matchit", 118 | "memchr", 119 | "mime", 120 | "percent-encoding", 121 | "pin-project-lite", 122 | "serde", 123 | "serde_json", 124 | "serde_urlencoded", 125 | "sha-1", 126 | "sync_wrapper", 127 | "tokio", 128 | "tokio-tungstenite", 129 | "tokio-util", 130 | "tower", 131 | "tower-http", 132 | "tower-layer", 133 | "tower-service", 134 | ] 135 | 136 | [[package]] 137 | name = "axum-core" 138 | version = "0.1.1" 139 | source = "registry+https://github.com/rust-lang/crates.io-index" 140 | checksum = "1ca6c0b218388a7ed6a8d25e94f7dea5498daaa4fd8c711fb3ff166041b06fda" 141 | dependencies = [ 142 | "async-trait", 143 | "bytes", 144 | "futures-util", 145 | "http", 146 | "http-body", 147 | "mime", 148 | ] 149 | 150 | [[package]] 151 | name = "base64" 152 | version = "0.13.0" 153 | source = "registry+https://github.com/rust-lang/crates.io-index" 154 | checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" 155 | 156 | [[package]] 157 | name = "bigdecimal" 158 | version = "0.3.0" 159 | source = "registry+https://github.com/rust-lang/crates.io-index" 160 | checksum = "6aaf33151a6429fe9211d1b276eafdf70cdff28b071e76c0b0e1503221ea3744" 161 | dependencies = [ 162 | "num-bigint", 163 | "num-integer", 164 | "num-traits", 165 | ] 166 | 167 | [[package]] 168 | name = "bitflags" 169 | version = "1.3.2" 170 | source = "registry+https://github.com/rust-lang/crates.io-index" 171 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 172 | 173 | [[package]] 174 | name = "block-buffer" 175 | version = "0.9.0" 176 | source = "registry+https://github.com/rust-lang/crates.io-index" 177 | checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" 178 | dependencies = [ 179 | "generic-array", 180 | ] 181 | 182 | [[package]] 183 | name = "byteorder" 184 | version = "1.4.3" 185 | source = "registry+https://github.com/rust-lang/crates.io-index" 186 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 187 | 188 | [[package]] 189 | name = "bytes" 190 | version = "1.1.0" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" 193 | 194 | [[package]] 195 | name = "cc" 196 | version = "1.0.73" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" 199 | 200 | [[package]] 201 | name = "cfg-if" 202 | version = "1.0.0" 203 | source = "registry+https://github.com/rust-lang/crates.io-index" 204 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 205 | 206 | [[package]] 207 | name = "chrono" 208 | version = "0.4.19" 209 | source = "registry+https://github.com/rust-lang/crates.io-index" 210 | checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" 211 | dependencies = [ 212 | "libc", 213 | "num-integer", 214 | "num-traits", 215 | "serde", 216 | "time", 217 | "winapi", 218 | ] 219 | 220 | [[package]] 221 | name = "chrono-tz" 222 | version = "0.6.1" 223 | source = "registry+https://github.com/rust-lang/crates.io-index" 224 | checksum = "58549f1842da3080ce63002102d5bc954c7bc843d4f47818e642abdc36253552" 225 | dependencies = [ 226 | "chrono", 227 | "chrono-tz-build", 228 | "phf", 229 | ] 230 | 231 | [[package]] 232 | name = "chrono-tz-build" 233 | version = "0.0.2" 234 | source = "registry+https://github.com/rust-lang/crates.io-index" 235 | checksum = "db058d493fb2f65f41861bfed7e3fe6335264a9f0f92710cab5bdf01fef09069" 236 | dependencies = [ 237 | "parse-zoneinfo", 238 | "phf", 239 | "phf_codegen", 240 | ] 241 | 242 | [[package]] 243 | name = "cpufeatures" 244 | version = "0.2.1" 245 | source = "registry+https://github.com/rust-lang/crates.io-index" 246 | checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" 247 | dependencies = [ 248 | "libc", 249 | ] 250 | 251 | [[package]] 252 | name = "diesel" 253 | version = "2.0.0-rc.0" 254 | source = "registry+https://github.com/rust-lang/crates.io-index" 255 | checksum = "e531d0abf8147036383c88ce3785d9cda6fe1b8b1183021ffa01cf49c5819d24" 256 | dependencies = [ 257 | "bigdecimal", 258 | "chrono", 259 | "diesel_derives", 260 | "libsqlite3-sys", 261 | "r2d2", 262 | ] 263 | 264 | [[package]] 265 | name = "diesel_derives" 266 | version = "2.0.2" 267 | source = "registry+https://github.com/rust-lang/crates.io-index" 268 | checksum = "0ad74fdcf086be3d4fdd142f67937678fe60ed431c3b2f08599e7687269410c4" 269 | dependencies = [ 270 | "proc-macro-error", 271 | "proc-macro2", 272 | "quote", 273 | "syn 1.0.81", 274 | ] 275 | 276 | [[package]] 277 | name = "diesel_migrations" 278 | version = "2.0.0-rc.0" 279 | source = "registry+https://github.com/rust-lang/crates.io-index" 280 | checksum = "e2edcb7e9cff76af48e7fa33cc4680acc7df19e58a4a4aaed3e2b67fb83bc06d" 281 | dependencies = [ 282 | "diesel", 283 | "migrations_internals", 284 | "migrations_macros", 285 | ] 286 | 287 | [[package]] 288 | name = "digest" 289 | version = "0.9.0" 290 | source = "registry+https://github.com/rust-lang/crates.io-index" 291 | checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" 292 | dependencies = [ 293 | "generic-array", 294 | ] 295 | 296 | [[package]] 297 | name = "dotenv" 298 | version = "0.15.0" 299 | source = "registry+https://github.com/rust-lang/crates.io-index" 300 | checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" 301 | 302 | [[package]] 303 | name = "event-listener" 304 | version = "2.5.2" 305 | source = "registry+https://github.com/rust-lang/crates.io-index" 306 | checksum = "77f3309417938f28bf8228fcff79a4a37103981e3e186d2ccd19c74b38f4eb71" 307 | 308 | [[package]] 309 | name = "fnv" 310 | version = "1.0.7" 311 | source = "registry+https://github.com/rust-lang/crates.io-index" 312 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 313 | 314 | [[package]] 315 | name = "form_urlencoded" 316 | version = "1.0.1" 317 | source = "registry+https://github.com/rust-lang/crates.io-index" 318 | checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" 319 | dependencies = [ 320 | "matches", 321 | "percent-encoding", 322 | ] 323 | 324 | [[package]] 325 | name = "futures-channel" 326 | version = "0.3.17" 327 | source = "registry+https://github.com/rust-lang/crates.io-index" 328 | checksum = "5da6ba8c3bb3c165d3c7319fc1cc8304facf1fb8db99c5de877183c08a273888" 329 | dependencies = [ 330 | "futures-core", 331 | ] 332 | 333 | [[package]] 334 | name = "futures-core" 335 | version = "0.3.17" 336 | source = "registry+https://github.com/rust-lang/crates.io-index" 337 | checksum = "88d1c26957f23603395cd326b0ffe64124b818f4449552f960d815cfba83a53d" 338 | 339 | [[package]] 340 | name = "futures-sink" 341 | version = "0.3.17" 342 | source = "registry+https://github.com/rust-lang/crates.io-index" 343 | checksum = "36ea153c13024fe480590b3e3d4cad89a0cfacecc24577b68f86c6ced9c2bc11" 344 | 345 | [[package]] 346 | name = "futures-task" 347 | version = "0.3.17" 348 | source = "registry+https://github.com/rust-lang/crates.io-index" 349 | checksum = "1d3d00f4eddb73e498a54394f228cd55853bdf059259e8e7bc6e69d408892e99" 350 | 351 | [[package]] 352 | name = "futures-util" 353 | version = "0.3.17" 354 | source = "registry+https://github.com/rust-lang/crates.io-index" 355 | checksum = "36568465210a3a6ee45e1f165136d68671471a501e632e9a98d96872222b5481" 356 | dependencies = [ 357 | "autocfg", 358 | "futures-core", 359 | "futures-sink", 360 | "futures-task", 361 | "pin-project-lite", 362 | "pin-utils", 363 | "slab", 364 | ] 365 | 366 | [[package]] 367 | name = "generic-array" 368 | version = "0.14.4" 369 | source = "registry+https://github.com/rust-lang/crates.io-index" 370 | checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" 371 | dependencies = [ 372 | "typenum", 373 | "version_check", 374 | ] 375 | 376 | [[package]] 377 | name = "getrandom" 378 | version = "0.2.4" 379 | source = "registry+https://github.com/rust-lang/crates.io-index" 380 | checksum = "418d37c8b1d42553c93648be529cb70f920d3baf8ef469b74b9638df426e0b4c" 381 | dependencies = [ 382 | "cfg-if", 383 | "libc", 384 | "wasi", 385 | ] 386 | 387 | [[package]] 388 | name = "headers" 389 | version = "0.3.5" 390 | source = "registry+https://github.com/rust-lang/crates.io-index" 391 | checksum = "a4c4eb0471fcb85846d8b0690695ef354f9afb11cb03cac2e1d7c9253351afb0" 392 | dependencies = [ 393 | "base64", 394 | "bitflags", 395 | "bytes", 396 | "headers-core", 397 | "http", 398 | "httpdate", 399 | "mime", 400 | "sha-1", 401 | ] 402 | 403 | [[package]] 404 | name = "headers-core" 405 | version = "0.2.0" 406 | source = "registry+https://github.com/rust-lang/crates.io-index" 407 | checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" 408 | dependencies = [ 409 | "http", 410 | ] 411 | 412 | [[package]] 413 | name = "hermit-abi" 414 | version = "0.1.19" 415 | source = "registry+https://github.com/rust-lang/crates.io-index" 416 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 417 | dependencies = [ 418 | "libc", 419 | ] 420 | 421 | [[package]] 422 | name = "http" 423 | version = "0.2.5" 424 | source = "registry+https://github.com/rust-lang/crates.io-index" 425 | checksum = "1323096b05d41827dadeaee54c9981958c0f94e670bc94ed80037d1a7b8b186b" 426 | dependencies = [ 427 | "bytes", 428 | "fnv", 429 | "itoa", 430 | ] 431 | 432 | [[package]] 433 | name = "http-body" 434 | version = "0.4.4" 435 | source = "registry+https://github.com/rust-lang/crates.io-index" 436 | checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" 437 | dependencies = [ 438 | "bytes", 439 | "http", 440 | "pin-project-lite", 441 | ] 442 | 443 | [[package]] 444 | name = "http-range-header" 445 | version = "0.3.0" 446 | source = "registry+https://github.com/rust-lang/crates.io-index" 447 | checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" 448 | 449 | [[package]] 450 | name = "httparse" 451 | version = "1.5.1" 452 | source = "registry+https://github.com/rust-lang/crates.io-index" 453 | checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503" 454 | 455 | [[package]] 456 | name = "httpdate" 457 | version = "1.0.1" 458 | source = "registry+https://github.com/rust-lang/crates.io-index" 459 | checksum = "6456b8a6c8f33fee7d958fcd1b60d55b11940a79e63ae87013e6d22e26034440" 460 | 461 | [[package]] 462 | name = "humansize" 463 | version = "1.1.1" 464 | source = "registry+https://github.com/rust-lang/crates.io-index" 465 | checksum = "02296996cb8796d7c6e3bc2d9211b7802812d36999a51bb754123ead7d37d026" 466 | 467 | [[package]] 468 | name = "hyper" 469 | version = "0.14.14" 470 | source = "registry+https://github.com/rust-lang/crates.io-index" 471 | checksum = "2b91bb1f221b6ea1f1e4371216b70f40748774c2fb5971b450c07773fb92d26b" 472 | dependencies = [ 473 | "bytes", 474 | "futures-channel", 475 | "futures-core", 476 | "futures-util", 477 | "http", 478 | "http-body", 479 | "httparse", 480 | "httpdate", 481 | "itoa", 482 | "pin-project-lite", 483 | "socket2", 484 | "tokio", 485 | "tower-service", 486 | "tracing", 487 | "want", 488 | ] 489 | 490 | [[package]] 491 | name = "idna" 492 | version = "0.2.3" 493 | source = "registry+https://github.com/rust-lang/crates.io-index" 494 | checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" 495 | dependencies = [ 496 | "matches", 497 | "unicode-bidi", 498 | "unicode-normalization", 499 | ] 500 | 501 | [[package]] 502 | name = "instant" 503 | version = "0.1.12" 504 | source = "registry+https://github.com/rust-lang/crates.io-index" 505 | checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" 506 | dependencies = [ 507 | "cfg-if", 508 | ] 509 | 510 | [[package]] 511 | name = "itoa" 512 | version = "0.4.8" 513 | source = "registry+https://github.com/rust-lang/crates.io-index" 514 | checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" 515 | 516 | [[package]] 517 | name = "lazy_static" 518 | version = "1.4.0" 519 | source = "registry+https://github.com/rust-lang/crates.io-index" 520 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 521 | 522 | [[package]] 523 | name = "libc" 524 | version = "0.2.107" 525 | source = "registry+https://github.com/rust-lang/crates.io-index" 526 | checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219" 527 | 528 | [[package]] 529 | name = "libsqlite3-sys" 530 | version = "0.22.2" 531 | source = "registry+https://github.com/rust-lang/crates.io-index" 532 | checksum = "290b64917f8b0cb885d9de0f9959fe1f775d7fa12f1da2db9001c1c8ab60f89d" 533 | dependencies = [ 534 | "cc", 535 | "pkg-config", 536 | "vcpkg", 537 | ] 538 | 539 | [[package]] 540 | name = "lock_api" 541 | version = "0.4.5" 542 | source = "registry+https://github.com/rust-lang/crates.io-index" 543 | checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" 544 | dependencies = [ 545 | "scopeguard", 546 | ] 547 | 548 | [[package]] 549 | name = "log" 550 | version = "0.4.14" 551 | source = "registry+https://github.com/rust-lang/crates.io-index" 552 | checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" 553 | dependencies = [ 554 | "cfg-if", 555 | ] 556 | 557 | [[package]] 558 | name = "matchers" 559 | version = "0.1.0" 560 | source = "registry+https://github.com/rust-lang/crates.io-index" 561 | checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" 562 | dependencies = [ 563 | "regex-automata", 564 | ] 565 | 566 | [[package]] 567 | name = "matches" 568 | version = "0.1.9" 569 | source = "registry+https://github.com/rust-lang/crates.io-index" 570 | checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" 571 | 572 | [[package]] 573 | name = "matchit" 574 | version = "0.4.4" 575 | source = "registry+https://github.com/rust-lang/crates.io-index" 576 | checksum = "58b6f41fdfbec185dd3dff58b51e323f5bc61692c0de38419a957b0dcfccca3c" 577 | 578 | [[package]] 579 | name = "memchr" 580 | version = "2.4.1" 581 | source = "registry+https://github.com/rust-lang/crates.io-index" 582 | checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" 583 | 584 | [[package]] 585 | name = "migrations_internals" 586 | version = "2.0.0-rc.0" 587 | source = "registry+https://github.com/rust-lang/crates.io-index" 588 | checksum = "61e2226081f9176165a49b006a0729de8cafa749615b349e15c444dbd39e4d9d" 589 | dependencies = [ 590 | "serde", 591 | "toml", 592 | ] 593 | 594 | [[package]] 595 | name = "migrations_macros" 596 | version = "2.0.0-rc.0" 597 | source = "registry+https://github.com/rust-lang/crates.io-index" 598 | checksum = "37dbadac6f186c134552cd9a0ec14b157cf4f04eacacebd5e6c1bfc6aeb158b3" 599 | dependencies = [ 600 | "migrations_internals", 601 | "proc-macro2", 602 | "quote", 603 | ] 604 | 605 | [[package]] 606 | name = "mime" 607 | version = "0.3.16" 608 | source = "registry+https://github.com/rust-lang/crates.io-index" 609 | checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" 610 | 611 | [[package]] 612 | name = "minimal-lexical" 613 | version = "0.2.1" 614 | source = "registry+https://github.com/rust-lang/crates.io-index" 615 | checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" 616 | 617 | [[package]] 618 | name = "mio" 619 | version = "0.7.14" 620 | source = "registry+https://github.com/rust-lang/crates.io-index" 621 | checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc" 622 | dependencies = [ 623 | "libc", 624 | "log", 625 | "miow", 626 | "ntapi", 627 | "winapi", 628 | ] 629 | 630 | [[package]] 631 | name = "miow" 632 | version = "0.3.7" 633 | source = "registry+https://github.com/rust-lang/crates.io-index" 634 | checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" 635 | dependencies = [ 636 | "winapi", 637 | ] 638 | 639 | [[package]] 640 | name = "naive" 641 | version = "0.1.0" 642 | dependencies = [ 643 | "anyhow", 644 | "askama", 645 | "axum", 646 | "chrono", 647 | "chrono-tz", 648 | "diesel", 649 | "diesel_migrations", 650 | "dotenv", 651 | "headers", 652 | "lazy_static", 653 | "libsqlite3-sys", 654 | "r-cache", 655 | "regex", 656 | "serde", 657 | "serde_json", 658 | "serde_repr", 659 | "tokio", 660 | "tracing", 661 | "tracing-subscriber", 662 | "url", 663 | ] 664 | 665 | [[package]] 666 | name = "nom" 667 | version = "7.1.0" 668 | source = "registry+https://github.com/rust-lang/crates.io-index" 669 | checksum = "1b1d11e1ef389c76fe5b81bcaf2ea32cf88b62bc494e19f493d0b30e7a930109" 670 | dependencies = [ 671 | "memchr", 672 | "minimal-lexical", 673 | "version_check", 674 | ] 675 | 676 | [[package]] 677 | name = "ntapi" 678 | version = "0.3.6" 679 | source = "registry+https://github.com/rust-lang/crates.io-index" 680 | checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" 681 | dependencies = [ 682 | "winapi", 683 | ] 684 | 685 | [[package]] 686 | name = "num-bigint" 687 | version = "0.4.3" 688 | source = "registry+https://github.com/rust-lang/crates.io-index" 689 | checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" 690 | dependencies = [ 691 | "autocfg", 692 | "num-integer", 693 | "num-traits", 694 | ] 695 | 696 | [[package]] 697 | name = "num-integer" 698 | version = "0.1.44" 699 | source = "registry+https://github.com/rust-lang/crates.io-index" 700 | checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" 701 | dependencies = [ 702 | "autocfg", 703 | "num-traits", 704 | ] 705 | 706 | [[package]] 707 | name = "num-traits" 708 | version = "0.2.14" 709 | source = "registry+https://github.com/rust-lang/crates.io-index" 710 | checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" 711 | dependencies = [ 712 | "autocfg", 713 | ] 714 | 715 | [[package]] 716 | name = "num_cpus" 717 | version = "1.13.0" 718 | source = "registry+https://github.com/rust-lang/crates.io-index" 719 | checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" 720 | dependencies = [ 721 | "hermit-abi", 722 | "libc", 723 | ] 724 | 725 | [[package]] 726 | name = "once_cell" 727 | version = "1.8.0" 728 | source = "registry+https://github.com/rust-lang/crates.io-index" 729 | checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" 730 | 731 | [[package]] 732 | name = "opaque-debug" 733 | version = "0.3.0" 734 | source = "registry+https://github.com/rust-lang/crates.io-index" 735 | checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" 736 | 737 | [[package]] 738 | name = "parking_lot" 739 | version = "0.11.2" 740 | source = "registry+https://github.com/rust-lang/crates.io-index" 741 | checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" 742 | dependencies = [ 743 | "instant", 744 | "lock_api", 745 | "parking_lot_core", 746 | ] 747 | 748 | [[package]] 749 | name = "parking_lot_core" 750 | version = "0.8.5" 751 | source = "registry+https://github.com/rust-lang/crates.io-index" 752 | checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" 753 | dependencies = [ 754 | "cfg-if", 755 | "instant", 756 | "libc", 757 | "redox_syscall", 758 | "smallvec", 759 | "winapi", 760 | ] 761 | 762 | [[package]] 763 | name = "parse-zoneinfo" 764 | version = "0.3.0" 765 | source = "registry+https://github.com/rust-lang/crates.io-index" 766 | checksum = "c705f256449c60da65e11ff6626e0c16a0a0b96aaa348de61376b249bc340f41" 767 | dependencies = [ 768 | "regex", 769 | ] 770 | 771 | [[package]] 772 | name = "percent-encoding" 773 | version = "2.1.0" 774 | source = "registry+https://github.com/rust-lang/crates.io-index" 775 | checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" 776 | 777 | [[package]] 778 | name = "phf" 779 | version = "0.10.1" 780 | source = "registry+https://github.com/rust-lang/crates.io-index" 781 | checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" 782 | dependencies = [ 783 | "phf_shared", 784 | ] 785 | 786 | [[package]] 787 | name = "phf_codegen" 788 | version = "0.10.0" 789 | source = "registry+https://github.com/rust-lang/crates.io-index" 790 | checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" 791 | dependencies = [ 792 | "phf_generator", 793 | "phf_shared", 794 | ] 795 | 796 | [[package]] 797 | name = "phf_generator" 798 | version = "0.10.0" 799 | source = "registry+https://github.com/rust-lang/crates.io-index" 800 | checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" 801 | dependencies = [ 802 | "phf_shared", 803 | "rand", 804 | ] 805 | 806 | [[package]] 807 | name = "phf_shared" 808 | version = "0.10.0" 809 | source = "registry+https://github.com/rust-lang/crates.io-index" 810 | checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" 811 | dependencies = [ 812 | "siphasher", 813 | "uncased", 814 | ] 815 | 816 | [[package]] 817 | name = "pin-project" 818 | version = "1.0.8" 819 | source = "registry+https://github.com/rust-lang/crates.io-index" 820 | checksum = "576bc800220cc65dac09e99e97b08b358cfab6e17078de8dc5fee223bd2d0c08" 821 | dependencies = [ 822 | "pin-project-internal", 823 | ] 824 | 825 | [[package]] 826 | name = "pin-project-internal" 827 | version = "1.0.8" 828 | source = "registry+https://github.com/rust-lang/crates.io-index" 829 | checksum = "6e8fe8163d14ce7f0cdac2e040116f22eac817edabff0be91e8aff7e9accf389" 830 | dependencies = [ 831 | "proc-macro2", 832 | "quote", 833 | "syn 1.0.81", 834 | ] 835 | 836 | [[package]] 837 | name = "pin-project-lite" 838 | version = "0.2.7" 839 | source = "registry+https://github.com/rust-lang/crates.io-index" 840 | checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" 841 | 842 | [[package]] 843 | name = "pin-utils" 844 | version = "0.1.0" 845 | source = "registry+https://github.com/rust-lang/crates.io-index" 846 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 847 | 848 | [[package]] 849 | name = "pkg-config" 850 | version = "0.3.22" 851 | source = "registry+https://github.com/rust-lang/crates.io-index" 852 | checksum = "12295df4f294471248581bc09bef3c38a5e46f1e36d6a37353621a0c6c357e1f" 853 | 854 | [[package]] 855 | name = "ppv-lite86" 856 | version = "0.2.16" 857 | source = "registry+https://github.com/rust-lang/crates.io-index" 858 | checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" 859 | 860 | [[package]] 861 | name = "proc-macro-error" 862 | version = "1.0.4" 863 | source = "registry+https://github.com/rust-lang/crates.io-index" 864 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 865 | dependencies = [ 866 | "proc-macro-error-attr", 867 | "proc-macro2", 868 | "quote", 869 | "syn 1.0.81", 870 | "version_check", 871 | ] 872 | 873 | [[package]] 874 | name = "proc-macro-error-attr" 875 | version = "1.0.4" 876 | source = "registry+https://github.com/rust-lang/crates.io-index" 877 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 878 | dependencies = [ 879 | "proc-macro2", 880 | "quote", 881 | "version_check", 882 | ] 883 | 884 | [[package]] 885 | name = "proc-macro2" 886 | version = "1.0.71" 887 | source = "registry+https://github.com/rust-lang/crates.io-index" 888 | checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8" 889 | dependencies = [ 890 | "unicode-ident", 891 | ] 892 | 893 | [[package]] 894 | name = "quote" 895 | version = "1.0.33" 896 | source = "registry+https://github.com/rust-lang/crates.io-index" 897 | checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" 898 | dependencies = [ 899 | "proc-macro2", 900 | ] 901 | 902 | [[package]] 903 | name = "r-cache" 904 | version = "0.4.4" 905 | source = "registry+https://github.com/rust-lang/crates.io-index" 906 | checksum = "5e950cbc471c9e185bdbc2f5e81439877805b7b7cf958af1058d15436918548d" 907 | dependencies = [ 908 | "async-lock", 909 | ] 910 | 911 | [[package]] 912 | name = "r2d2" 913 | version = "0.8.9" 914 | source = "registry+https://github.com/rust-lang/crates.io-index" 915 | checksum = "545c5bc2b880973c9c10e4067418407a0ccaa3091781d1671d46eb35107cb26f" 916 | dependencies = [ 917 | "log", 918 | "parking_lot", 919 | "scheduled-thread-pool", 920 | ] 921 | 922 | [[package]] 923 | name = "rand" 924 | version = "0.8.4" 925 | source = "registry+https://github.com/rust-lang/crates.io-index" 926 | checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" 927 | dependencies = [ 928 | "libc", 929 | "rand_chacha", 930 | "rand_core", 931 | "rand_hc", 932 | ] 933 | 934 | [[package]] 935 | name = "rand_chacha" 936 | version = "0.3.1" 937 | source = "registry+https://github.com/rust-lang/crates.io-index" 938 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 939 | dependencies = [ 940 | "ppv-lite86", 941 | "rand_core", 942 | ] 943 | 944 | [[package]] 945 | name = "rand_core" 946 | version = "0.6.3" 947 | source = "registry+https://github.com/rust-lang/crates.io-index" 948 | checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" 949 | dependencies = [ 950 | "getrandom", 951 | ] 952 | 953 | [[package]] 954 | name = "rand_hc" 955 | version = "0.3.1" 956 | source = "registry+https://github.com/rust-lang/crates.io-index" 957 | checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" 958 | dependencies = [ 959 | "rand_core", 960 | ] 961 | 962 | [[package]] 963 | name = "redox_syscall" 964 | version = "0.2.10" 965 | source = "registry+https://github.com/rust-lang/crates.io-index" 966 | checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" 967 | dependencies = [ 968 | "bitflags", 969 | ] 970 | 971 | [[package]] 972 | name = "regex" 973 | version = "1.5.4" 974 | source = "registry+https://github.com/rust-lang/crates.io-index" 975 | checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" 976 | dependencies = [ 977 | "aho-corasick", 978 | "memchr", 979 | "regex-syntax", 980 | ] 981 | 982 | [[package]] 983 | name = "regex-automata" 984 | version = "0.1.10" 985 | source = "registry+https://github.com/rust-lang/crates.io-index" 986 | checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" 987 | dependencies = [ 988 | "regex-syntax", 989 | ] 990 | 991 | [[package]] 992 | name = "regex-syntax" 993 | version = "0.6.25" 994 | source = "registry+https://github.com/rust-lang/crates.io-index" 995 | checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" 996 | 997 | [[package]] 998 | name = "ryu" 999 | version = "1.0.5" 1000 | source = "registry+https://github.com/rust-lang/crates.io-index" 1001 | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" 1002 | 1003 | [[package]] 1004 | name = "scheduled-thread-pool" 1005 | version = "0.2.5" 1006 | source = "registry+https://github.com/rust-lang/crates.io-index" 1007 | checksum = "dc6f74fd1204073fa02d5d5d68bec8021be4c38690b61264b2fdb48083d0e7d7" 1008 | dependencies = [ 1009 | "parking_lot", 1010 | ] 1011 | 1012 | [[package]] 1013 | name = "scopeguard" 1014 | version = "1.1.0" 1015 | source = "registry+https://github.com/rust-lang/crates.io-index" 1016 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 1017 | 1018 | [[package]] 1019 | name = "serde" 1020 | version = "1.0.130" 1021 | source = "registry+https://github.com/rust-lang/crates.io-index" 1022 | checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" 1023 | dependencies = [ 1024 | "serde_derive", 1025 | ] 1026 | 1027 | [[package]] 1028 | name = "serde_derive" 1029 | version = "1.0.130" 1030 | source = "registry+https://github.com/rust-lang/crates.io-index" 1031 | checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" 1032 | dependencies = [ 1033 | "proc-macro2", 1034 | "quote", 1035 | "syn 1.0.81", 1036 | ] 1037 | 1038 | [[package]] 1039 | name = "serde_json" 1040 | version = "1.0.70" 1041 | source = "registry+https://github.com/rust-lang/crates.io-index" 1042 | checksum = "e277c495ac6cd1a01a58d0a0c574568b4d1ddf14f59965c6a58b8d96400b54f3" 1043 | dependencies = [ 1044 | "itoa", 1045 | "ryu", 1046 | "serde", 1047 | ] 1048 | 1049 | [[package]] 1050 | name = "serde_repr" 1051 | version = "0.1.17" 1052 | source = "registry+https://github.com/rust-lang/crates.io-index" 1053 | checksum = "3081f5ffbb02284dda55132aa26daecedd7372a42417bbbab6f14ab7d6bb9145" 1054 | dependencies = [ 1055 | "proc-macro2", 1056 | "quote", 1057 | "syn 2.0.43", 1058 | ] 1059 | 1060 | [[package]] 1061 | name = "serde_urlencoded" 1062 | version = "0.7.0" 1063 | source = "registry+https://github.com/rust-lang/crates.io-index" 1064 | checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" 1065 | dependencies = [ 1066 | "form_urlencoded", 1067 | "itoa", 1068 | "ryu", 1069 | "serde", 1070 | ] 1071 | 1072 | [[package]] 1073 | name = "sha-1" 1074 | version = "0.9.8" 1075 | source = "registry+https://github.com/rust-lang/crates.io-index" 1076 | checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" 1077 | dependencies = [ 1078 | "block-buffer", 1079 | "cfg-if", 1080 | "cpufeatures", 1081 | "digest", 1082 | "opaque-debug", 1083 | ] 1084 | 1085 | [[package]] 1086 | name = "sharded-slab" 1087 | version = "0.1.4" 1088 | source = "registry+https://github.com/rust-lang/crates.io-index" 1089 | checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" 1090 | dependencies = [ 1091 | "lazy_static", 1092 | ] 1093 | 1094 | [[package]] 1095 | name = "signal-hook-registry" 1096 | version = "1.4.0" 1097 | source = "registry+https://github.com/rust-lang/crates.io-index" 1098 | checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" 1099 | dependencies = [ 1100 | "libc", 1101 | ] 1102 | 1103 | [[package]] 1104 | name = "siphasher" 1105 | version = "0.3.9" 1106 | source = "registry+https://github.com/rust-lang/crates.io-index" 1107 | checksum = "a86232ab60fa71287d7f2ddae4a7073f6b7aac33631c3015abb556f08c6d0a3e" 1108 | 1109 | [[package]] 1110 | name = "slab" 1111 | version = "0.4.5" 1112 | source = "registry+https://github.com/rust-lang/crates.io-index" 1113 | checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" 1114 | 1115 | [[package]] 1116 | name = "smallvec" 1117 | version = "1.7.0" 1118 | source = "registry+https://github.com/rust-lang/crates.io-index" 1119 | checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309" 1120 | 1121 | [[package]] 1122 | name = "socket2" 1123 | version = "0.4.2" 1124 | source = "registry+https://github.com/rust-lang/crates.io-index" 1125 | checksum = "5dc90fe6c7be1a323296982db1836d1ea9e47b6839496dde9a541bc496df3516" 1126 | dependencies = [ 1127 | "libc", 1128 | "winapi", 1129 | ] 1130 | 1131 | [[package]] 1132 | name = "syn" 1133 | version = "1.0.81" 1134 | source = "registry+https://github.com/rust-lang/crates.io-index" 1135 | checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966" 1136 | dependencies = [ 1137 | "proc-macro2", 1138 | "quote", 1139 | "unicode-xid", 1140 | ] 1141 | 1142 | [[package]] 1143 | name = "syn" 1144 | version = "2.0.43" 1145 | source = "registry+https://github.com/rust-lang/crates.io-index" 1146 | checksum = "ee659fb5f3d355364e1f3e5bc10fb82068efbf824a1e9d1c9504244a6469ad53" 1147 | dependencies = [ 1148 | "proc-macro2", 1149 | "quote", 1150 | "unicode-ident", 1151 | ] 1152 | 1153 | [[package]] 1154 | name = "sync_wrapper" 1155 | version = "0.1.1" 1156 | source = "registry+https://github.com/rust-lang/crates.io-index" 1157 | checksum = "20518fe4a4c9acf048008599e464deb21beeae3d3578418951a189c235a7a9a8" 1158 | 1159 | [[package]] 1160 | name = "thiserror" 1161 | version = "1.0.30" 1162 | source = "registry+https://github.com/rust-lang/crates.io-index" 1163 | checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" 1164 | dependencies = [ 1165 | "thiserror-impl", 1166 | ] 1167 | 1168 | [[package]] 1169 | name = "thiserror-impl" 1170 | version = "1.0.30" 1171 | source = "registry+https://github.com/rust-lang/crates.io-index" 1172 | checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" 1173 | dependencies = [ 1174 | "proc-macro2", 1175 | "quote", 1176 | "syn 1.0.81", 1177 | ] 1178 | 1179 | [[package]] 1180 | name = "thread_local" 1181 | version = "1.1.3" 1182 | source = "registry+https://github.com/rust-lang/crates.io-index" 1183 | checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd" 1184 | dependencies = [ 1185 | "once_cell", 1186 | ] 1187 | 1188 | [[package]] 1189 | name = "time" 1190 | version = "0.1.44" 1191 | source = "registry+https://github.com/rust-lang/crates.io-index" 1192 | checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" 1193 | dependencies = [ 1194 | "libc", 1195 | "wasi", 1196 | "winapi", 1197 | ] 1198 | 1199 | [[package]] 1200 | name = "tinyvec" 1201 | version = "1.5.1" 1202 | source = "registry+https://github.com/rust-lang/crates.io-index" 1203 | checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2" 1204 | dependencies = [ 1205 | "tinyvec_macros", 1206 | ] 1207 | 1208 | [[package]] 1209 | name = "tinyvec_macros" 1210 | version = "0.1.0" 1211 | source = "registry+https://github.com/rust-lang/crates.io-index" 1212 | checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" 1213 | 1214 | [[package]] 1215 | name = "tokio" 1216 | version = "1.13.0" 1217 | source = "registry+https://github.com/rust-lang/crates.io-index" 1218 | checksum = "588b2d10a336da58d877567cd8fb8a14b463e2104910f8132cd054b4b96e29ee" 1219 | dependencies = [ 1220 | "autocfg", 1221 | "bytes", 1222 | "libc", 1223 | "memchr", 1224 | "mio", 1225 | "num_cpus", 1226 | "once_cell", 1227 | "parking_lot", 1228 | "pin-project-lite", 1229 | "signal-hook-registry", 1230 | "tokio-macros", 1231 | "winapi", 1232 | ] 1233 | 1234 | [[package]] 1235 | name = "tokio-macros" 1236 | version = "1.5.1" 1237 | source = "registry+https://github.com/rust-lang/crates.io-index" 1238 | checksum = "114383b041aa6212c579467afa0075fbbdd0718de036100bc0ba7961d8cb9095" 1239 | dependencies = [ 1240 | "proc-macro2", 1241 | "quote", 1242 | "syn 1.0.81", 1243 | ] 1244 | 1245 | [[package]] 1246 | name = "tokio-tungstenite" 1247 | version = "0.16.1" 1248 | source = "registry+https://github.com/rust-lang/crates.io-index" 1249 | checksum = "e80b39df6afcc12cdf752398ade96a6b9e99c903dfdc36e53ad10b9c366bca72" 1250 | dependencies = [ 1251 | "futures-util", 1252 | "log", 1253 | "tokio", 1254 | "tungstenite", 1255 | ] 1256 | 1257 | [[package]] 1258 | name = "tokio-util" 1259 | version = "0.6.9" 1260 | source = "registry+https://github.com/rust-lang/crates.io-index" 1261 | checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0" 1262 | dependencies = [ 1263 | "bytes", 1264 | "futures-core", 1265 | "futures-sink", 1266 | "log", 1267 | "pin-project-lite", 1268 | "tokio", 1269 | ] 1270 | 1271 | [[package]] 1272 | name = "toml" 1273 | version = "0.5.8" 1274 | source = "registry+https://github.com/rust-lang/crates.io-index" 1275 | checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" 1276 | dependencies = [ 1277 | "serde", 1278 | ] 1279 | 1280 | [[package]] 1281 | name = "tower" 1282 | version = "0.4.11" 1283 | source = "registry+https://github.com/rust-lang/crates.io-index" 1284 | checksum = "5651b5f6860a99bd1adb59dbfe1db8beb433e73709d9032b413a77e2fb7c066a" 1285 | dependencies = [ 1286 | "futures-core", 1287 | "futures-util", 1288 | "pin-project", 1289 | "pin-project-lite", 1290 | "tokio", 1291 | "tokio-util", 1292 | "tower-layer", 1293 | "tower-service", 1294 | "tracing", 1295 | ] 1296 | 1297 | [[package]] 1298 | name = "tower-http" 1299 | version = "0.2.0" 1300 | source = "registry+https://github.com/rust-lang/crates.io-index" 1301 | checksum = "39ee603d6e665ecc7e0f8d479eedb4626bd4726f0ee6119cee5b3a6bf184cac0" 1302 | dependencies = [ 1303 | "bitflags", 1304 | "bytes", 1305 | "futures-core", 1306 | "futures-util", 1307 | "http", 1308 | "http-body", 1309 | "http-range-header", 1310 | "pin-project-lite", 1311 | "tower-layer", 1312 | "tower-service", 1313 | ] 1314 | 1315 | [[package]] 1316 | name = "tower-layer" 1317 | version = "0.3.1" 1318 | source = "registry+https://github.com/rust-lang/crates.io-index" 1319 | checksum = "343bc9466d3fe6b0f960ef45960509f84480bf4fd96f92901afe7ff3df9d3a62" 1320 | 1321 | [[package]] 1322 | name = "tower-service" 1323 | version = "0.3.1" 1324 | source = "registry+https://github.com/rust-lang/crates.io-index" 1325 | checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" 1326 | 1327 | [[package]] 1328 | name = "tracing" 1329 | version = "0.1.29" 1330 | source = "registry+https://github.com/rust-lang/crates.io-index" 1331 | checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105" 1332 | dependencies = [ 1333 | "cfg-if", 1334 | "log", 1335 | "pin-project-lite", 1336 | "tracing-attributes", 1337 | "tracing-core", 1338 | ] 1339 | 1340 | [[package]] 1341 | name = "tracing-attributes" 1342 | version = "0.1.18" 1343 | source = "registry+https://github.com/rust-lang/crates.io-index" 1344 | checksum = "f4f480b8f81512e825f337ad51e94c1eb5d3bbdf2b363dcd01e2b19a9ffe3f8e" 1345 | dependencies = [ 1346 | "proc-macro2", 1347 | "quote", 1348 | "syn 1.0.81", 1349 | ] 1350 | 1351 | [[package]] 1352 | name = "tracing-core" 1353 | version = "0.1.21" 1354 | source = "registry+https://github.com/rust-lang/crates.io-index" 1355 | checksum = "1f4ed65637b8390770814083d20756f87bfa2c21bf2f110babdc5438351746e4" 1356 | dependencies = [ 1357 | "lazy_static", 1358 | ] 1359 | 1360 | [[package]] 1361 | name = "tracing-log" 1362 | version = "0.1.2" 1363 | source = "registry+https://github.com/rust-lang/crates.io-index" 1364 | checksum = "a6923477a48e41c1951f1999ef8bb5a3023eb723ceadafe78ffb65dc366761e3" 1365 | dependencies = [ 1366 | "lazy_static", 1367 | "log", 1368 | "tracing-core", 1369 | ] 1370 | 1371 | [[package]] 1372 | name = "tracing-subscriber" 1373 | version = "0.3.1" 1374 | source = "registry+https://github.com/rust-lang/crates.io-index" 1375 | checksum = "80a4ddde70311d8da398062ecf6fc2c309337de6b0f77d6c27aff8d53f6fca52" 1376 | dependencies = [ 1377 | "ansi_term", 1378 | "lazy_static", 1379 | "matchers", 1380 | "regex", 1381 | "sharded-slab", 1382 | "smallvec", 1383 | "thread_local", 1384 | "tracing", 1385 | "tracing-core", 1386 | "tracing-log", 1387 | ] 1388 | 1389 | [[package]] 1390 | name = "try-lock" 1391 | version = "0.2.3" 1392 | source = "registry+https://github.com/rust-lang/crates.io-index" 1393 | checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" 1394 | 1395 | [[package]] 1396 | name = "tungstenite" 1397 | version = "0.16.0" 1398 | source = "registry+https://github.com/rust-lang/crates.io-index" 1399 | checksum = "6ad3713a14ae247f22a728a0456a545df14acf3867f905adff84be99e23b3ad1" 1400 | dependencies = [ 1401 | "base64", 1402 | "byteorder", 1403 | "bytes", 1404 | "http", 1405 | "httparse", 1406 | "log", 1407 | "rand", 1408 | "sha-1", 1409 | "thiserror", 1410 | "url", 1411 | "utf-8", 1412 | ] 1413 | 1414 | [[package]] 1415 | name = "typenum" 1416 | version = "1.14.0" 1417 | source = "registry+https://github.com/rust-lang/crates.io-index" 1418 | checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec" 1419 | 1420 | [[package]] 1421 | name = "uncased" 1422 | version = "0.9.6" 1423 | source = "registry+https://github.com/rust-lang/crates.io-index" 1424 | checksum = "5baeed7327e25054889b9bd4f975f32e5f4c5d434042d59ab6cd4142c0a76ed0" 1425 | dependencies = [ 1426 | "version_check", 1427 | ] 1428 | 1429 | [[package]] 1430 | name = "unicode-bidi" 1431 | version = "0.3.7" 1432 | source = "registry+https://github.com/rust-lang/crates.io-index" 1433 | checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" 1434 | 1435 | [[package]] 1436 | name = "unicode-ident" 1437 | version = "1.0.12" 1438 | source = "registry+https://github.com/rust-lang/crates.io-index" 1439 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 1440 | 1441 | [[package]] 1442 | name = "unicode-normalization" 1443 | version = "0.1.19" 1444 | source = "registry+https://github.com/rust-lang/crates.io-index" 1445 | checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" 1446 | dependencies = [ 1447 | "tinyvec", 1448 | ] 1449 | 1450 | [[package]] 1451 | name = "unicode-xid" 1452 | version = "0.2.2" 1453 | source = "registry+https://github.com/rust-lang/crates.io-index" 1454 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 1455 | 1456 | [[package]] 1457 | name = "url" 1458 | version = "2.2.2" 1459 | source = "registry+https://github.com/rust-lang/crates.io-index" 1460 | checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" 1461 | dependencies = [ 1462 | "form_urlencoded", 1463 | "idna", 1464 | "matches", 1465 | "percent-encoding", 1466 | ] 1467 | 1468 | [[package]] 1469 | name = "utf-8" 1470 | version = "0.7.6" 1471 | source = "registry+https://github.com/rust-lang/crates.io-index" 1472 | checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" 1473 | 1474 | [[package]] 1475 | name = "vcpkg" 1476 | version = "0.2.15" 1477 | source = "registry+https://github.com/rust-lang/crates.io-index" 1478 | checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" 1479 | 1480 | [[package]] 1481 | name = "version_check" 1482 | version = "0.9.3" 1483 | source = "registry+https://github.com/rust-lang/crates.io-index" 1484 | checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" 1485 | 1486 | [[package]] 1487 | name = "want" 1488 | version = "0.3.0" 1489 | source = "registry+https://github.com/rust-lang/crates.io-index" 1490 | checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" 1491 | dependencies = [ 1492 | "log", 1493 | "try-lock", 1494 | ] 1495 | 1496 | [[package]] 1497 | name = "wasi" 1498 | version = "0.10.0+wasi-snapshot-preview1" 1499 | source = "registry+https://github.com/rust-lang/crates.io-index" 1500 | checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" 1501 | 1502 | [[package]] 1503 | name = "winapi" 1504 | version = "0.3.9" 1505 | source = "registry+https://github.com/rust-lang/crates.io-index" 1506 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1507 | dependencies = [ 1508 | "winapi-i686-pc-windows-gnu", 1509 | "winapi-x86_64-pc-windows-gnu", 1510 | ] 1511 | 1512 | [[package]] 1513 | name = "winapi-i686-pc-windows-gnu" 1514 | version = "0.4.0" 1515 | source = "registry+https://github.com/rust-lang/crates.io-index" 1516 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1517 | 1518 | [[package]] 1519 | name = "winapi-x86_64-pc-windows-gnu" 1520 | version = "0.4.0" 1521 | source = "registry+https://github.com/rust-lang/crates.io-index" 1522 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1523 | --------------------------------------------------------------------------------