最小可用版
This commit is contained in:
commit
8efd10d765
|
@ -0,0 +1,2 @@
|
|||
/target
|
||||
/southbay.sqlite3
|
|
@ -0,0 +1,654 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "accounts"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"openssl",
|
||||
"rusqlite",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
|
||||
dependencies = [
|
||||
"gimli",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "adler"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.8.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42cd52102d3df161c77a887b608d7a4897d7cc112886a9537b738a887a03aaff"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96b09b5178381e0874812a9b157f7fe84982617e48f71f4e3235482775e5b540"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
|
||||
dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"miniz_oxide",
|
||||
"object",
|
||||
"rustc-demangle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||
|
||||
[[package]]
|
||||
name = "fallible-iterator"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
|
||||
|
||||
[[package]]
|
||||
name = "fallible-streaming-iterator"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
|
||||
dependencies = [
|
||||
"foreign-types-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types-shared"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.28.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"allocator-api2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashlink"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "692eaaf7f7607518dd3cef090f1474b61edc5301d8012f09579920df68b725ee"
|
||||
dependencies = [
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.153"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
|
||||
|
||||
[[package]]
|
||||
name = "libsqlite3-sys"
|
||||
version = "0.28.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c10584274047cb335c23d3e61bcef8e323adae7c5c8c760540f73610177fc3f"
|
||||
dependencies = [
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7"
|
||||
dependencies = [
|
||||
"adler",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.8.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.32.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15c9d69dd87a29568d4d017cfe8ec518706046a05184e5aea92d0af890b803c8"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"foreign-types",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"openssl-macros",
|
||||
"openssl-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-macros"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22e1bf214306098e4832460f797824c05d25aacdf896f64a985fb0fd992454ae"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.78"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rusqlite"
|
||||
version = "0.31.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b838eba278d213a8beaf485bd313fd580ca4505a00d5871caeb1457c55322cae"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"fallible-iterator",
|
||||
"fallible-streaming-iterator",
|
||||
"hashlink",
|
||||
"libsqlite3-sys",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.49"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "915aea9e586f80826ee59f8453c1101f9d1c4b3964cd2460185ee8e299ada496"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.36.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
"libc",
|
||||
"mio",
|
||||
"num_cpus",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||
dependencies = [
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.48.5",
|
||||
"windows_aarch64_msvc 0.48.5",
|
||||
"windows_i686_gnu 0.48.5",
|
||||
"windows_i686_msvc 0.48.5",
|
||||
"windows_x86_64_gnu 0.48.5",
|
||||
"windows_x86_64_gnullvm 0.48.5",
|
||||
"windows_x86_64_msvc 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.52.0",
|
||||
"windows_aarch64_msvc 0.52.0",
|
||||
"windows_i686_gnu 0.52.0",
|
||||
"windows_i686_msvc 0.52.0",
|
||||
"windows_x86_64_gnu 0.52.0",
|
||||
"windows_x86_64_gnullvm 0.52.0",
|
||||
"windows_x86_64_msvc 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.7.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
|
@ -0,0 +1,12 @@
|
|||
[package]
|
||||
name = "accounts"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.5.1", features = ["derive"] }
|
||||
openssl = "0.10.63"
|
||||
rusqlite = "0.31.0"
|
||||
tokio = { version = "1.36.0", features = ["net", "io-std", "sync", "time", "signal", "rt", "rt-multi-thread", "macros", "io-util"] }
|
|
@ -0,0 +1,61 @@
|
|||
# SouthBay Accounts
|
||||
|
||||
南湾用户登录服务。
|
||||
|
||||
## 登录流程
|
||||
|
||||
用户只能每3秒发送一次登录请求。每3秒发送超过30次登录请求视为攻击行为,禁止请求的ip`3600`秒。
|
||||
|
||||
客户端依次发送以下三个请求。
|
||||
|
||||
每个请求都先发送一个字节表示请求编号。
|
||||
|
||||
* 编号1`prelogin`:客户端向服务端请求tcp连接并发送私钥加密的`southbayhub login:{id}`,
|
||||
服务端回复一个公钥加密的`southbayhub server`、`already logged`或`unexist`,连接断开。
|
||||
服务端账户由`unlinked`状态进入`logining`状态10秒,超过10秒视为登录失败。
|
||||
客户端通过解密字符串验证服务端的有效性,并选择停止登录或继续登录。
|
||||
* 编号2`login`:客户端向服务端请求tcp连接并发送私钥加密的`{id}|{passwd}`,
|
||||
服务端根据是否超时以及账号和密码的正确性回复公钥加密的`unprelogged`、`logged in`、
|
||||
`passwd incorrect`,连接断开。
|
||||
服务端账户进入`logged`状态60秒,超过60秒进入`nonavailable`状态。
|
||||
* 编号3`heartbeat`:客户端向服务端请求tcp连接并发送私钥加密的`{id}`后服务端的`logged`状态计时归零,
|
||||
处于`unavailable`状态的换回`logged`状态并将计时归零。处于其它状态的什么都不做。
|
||||
* 编号4`logout`:客户端向服务端请求tcp连接并发送私钥加密的`{id}`。所有的用户数据项重新写回数据库。
|
||||
|
||||
## 数据库
|
||||
|
||||
使用`sqlite3`。
|
||||
|
||||
### 用户数据项
|
||||
|
||||
项目|说明
|
||||
:-:|:-
|
||||
`id`|用户唯一标识符,用户名的md5前8字节。
|
||||
`name`|用户名,不可以更改。
|
||||
`passwd`|用户密码的sha512值字符串的md5值,客户端应发送sha512,数据表应存储sha512值的md5值16进制字符串。
|
||||
`email`|用户邮箱。
|
||||
`avatar`|用户头像。一个服务端资源id。
|
||||
|
||||
```sql
|
||||
CREATE TABLE users (
|
||||
id BIGINT PRIMARY KEY,
|
||||
name VARCHAR(128),
|
||||
passwd CHAR(32),
|
||||
email TEXT,
|
||||
avatar INT
|
||||
);
|
||||
```
|
||||
|
||||
### 资源数据项
|
||||
|
||||
项目|说明
|
||||
:-:|:-
|
||||
`id`|资源id
|
||||
`path`|资源路径
|
||||
|
||||
```sql
|
||||
CREATE TABLE resource (
|
||||
id INT PRIMARY KEY,
|
||||
path TEXT
|
||||
);
|
||||
```
|
|
@ -0,0 +1,12 @@
|
|||
use clap::Parser;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
pub struct Config {
|
||||
/// 监听端口
|
||||
#[arg(short, long, default_value_t = 8848)]
|
||||
pub port: usize,
|
||||
/// 并行数
|
||||
#[arg(short, long, default_value_t = 4)]
|
||||
pub concurrent: usize,
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
use openssl::{error::ErrorStack, pkey::Private, rsa::Rsa};
|
||||
|
||||
static mut PRIKEY: Option<Box<Rsa<Private>>> = None;
|
||||
|
||||
pub struct Crypt {}
|
||||
|
||||
impl Crypt {
|
||||
pub fn init() -> Result<(), ErrorStack> {
|
||||
let prikey = include_str!("id_rsa");
|
||||
let prikey = Rsa::private_key_from_pem(prikey.as_bytes())?;
|
||||
unsafe { PRIKEY = Some(Box::new(prikey)) };
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn encrypt(text: &str) -> Vec<u8> {
|
||||
let mut ciphertext = vec![0; unsafe { PRIKEY.as_ref().unwrap() }.size() as usize];
|
||||
let encrypted_len = unsafe { PRIKEY.as_ref().unwrap() }
|
||||
.public_encrypt(
|
||||
text.as_bytes(),
|
||||
&mut ciphertext,
|
||||
openssl::rsa::Padding::PKCS1,
|
||||
)
|
||||
.unwrap();
|
||||
ciphertext.truncate(encrypted_len);
|
||||
ciphertext
|
||||
}
|
||||
|
||||
pub fn decrypt(data: &[u8]) -> String {
|
||||
let mut ciphertext = vec![0; unsafe { PRIKEY.as_ref().unwrap() }.size() as usize];
|
||||
let encrypted_len = unsafe { PRIKEY.as_ref().unwrap() }
|
||||
.public_decrypt(data, &mut ciphertext, openssl::rsa::Padding::PKCS1)
|
||||
.unwrap();
|
||||
ciphertext.truncate(encrypted_len);
|
||||
ciphertext
|
||||
.into_iter()
|
||||
.map(|x| x as char)
|
||||
.collect::<String>()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
use std::{
|
||||
cell::RefCell,
|
||||
io::{self, ErrorKind},
|
||||
};
|
||||
|
||||
use rusqlite::Connection;
|
||||
|
||||
use crate::userctl::{OnlineState, User};
|
||||
|
||||
thread_local! {
|
||||
static CONNECTION: RefCell<Connection> = RefCell::new(Connection::open("southbay.sqlite3").unwrap());
|
||||
}
|
||||
|
||||
pub async fn read_user(id: usize) -> Result<User, Box<io::Error>> {
|
||||
let res = CONNECTION.with(|conn| -> Result<User, Box<io::Error>> {
|
||||
let conn = conn.borrow();
|
||||
let mut statement = conn
|
||||
.prepare("SELECT name, passwd, email, avatar FROM users IF id = ?1;")
|
||||
.unwrap();
|
||||
let mut q = statement.query([id]).unwrap();
|
||||
let row = if let Ok(row) = q.next() {
|
||||
row
|
||||
} else {
|
||||
return Err(Box::new(io::Error::new(
|
||||
io::ErrorKind::NotFound,
|
||||
format!("{}", id),
|
||||
)));
|
||||
}
|
||||
.unwrap();
|
||||
Ok(User {
|
||||
id,
|
||||
name: row.get(0).unwrap(),
|
||||
passwd: row.get(1).unwrap(),
|
||||
email: row.get(2).unwrap(),
|
||||
avatar: row.get(3).unwrap(),
|
||||
state: OnlineState::Logging,
|
||||
})
|
||||
})?;
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
pub async fn save_user(user: &User) -> io::Result<()> {
|
||||
CONNECTION.with(|conn| -> io::Result<()> {
|
||||
let conn = conn.borrow();
|
||||
let mut statement = conn
|
||||
.prepare("INSERT INTO users (name, passwd, email, avatar) VALUES (?1, ?2, ?3, ?4);")
|
||||
.unwrap();
|
||||
let mut q = statement
|
||||
.query([
|
||||
user.name.as_bytes(),
|
||||
&user.passwd,
|
||||
user.email.as_bytes(),
|
||||
&user.avatar.to_le_bytes(),
|
||||
])
|
||||
.unwrap();
|
||||
if let Ok(_) = q.next() {
|
||||
()
|
||||
} else {
|
||||
return Err(io::Error::new(
|
||||
ErrorKind::NotFound,
|
||||
format!("Unexist user {}", user.id),
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC0zG3gOS43O72S
|
||||
bL/l3RWtP3eHlxAbn0fT13tfzrFXa3IyWSg4b1rtrEUqgO8ZLaYDpqeYuszLqcW9
|
||||
6r45l5CGqY3Iuf7RSmlmM3SI3R5x6QefsR0KWj7pdTJZ6uZzJb49e9PcQffQWQuk
|
||||
tFH0Y6YpSOfH7frBTj/ipv51NkniRgh6pTUeBv9vFsvmm4HIhPhtle7L+wlTdRuQ
|
||||
X+MVPH1kYqRBb7PioQONNEuefKRG4MnPtIb9mqtJ92EanzELnal9ypG8ZtajFFpx
|
||||
uE18wIT+/n4CTR0q4dQyrbphVfEW4fwvtcJeQoKLG5m8QjrDqcQPv3chvZyaHfmz
|
||||
S63A4eQjAgMBAAECggEAEQGxzDJUwJP5WblVC7WSK8EZPe3dxrC4PEW8Bd/BcBcG
|
||||
n5R3Y2ebcVk6YFcke9p+upUwlVNCSbNnNbLLwKBwfdpGdf0uP62fiyIynLUqqfhg
|
||||
49/werV7ztCxXc0/btFmuw3XeJPT+wsv5zGhtpAGZJfGWwNttO/ms9aVf50Nsh+l
|
||||
LKfBe87gOcfEz4706lXtVHpyBtsRg+pZSIA4tF6TH3PgUHWUAj7dsC9+jd7MGHnl
|
||||
z+dOemTNtjqqk7d7j71QiKe+ChP6bCxdv6fUQom0Y3XkeSKwHxVF2JATh7JN1c0e
|
||||
KTJdSRliw8Snd6ccEQPOII5k05sPhY70Ijq28CnBIQKBgQDb7iBH68mCvb+OJTC9
|
||||
ijYA1qvGm5IJqeaN+Ac2q4Hft4kDl1DiS2j4+l291MQ7FH3szMuV07AqXkDHWzUQ
|
||||
i/5wlP5Y6EFlJXyryfTM8Mz9v8XFMGkJXx3EU9nRpCkMEOhQc4grngnCMEayz2F+
|
||||
g+Y6I100eqqu6smA38Yap0l2ywKBgQDSc1fLd34Ew2QzZtevnroQtdkG4sO7y0hL
|
||||
iCvikTy6KVykHHZkvfDsE6k7JeG+2wqXuZUKrHqgIkOXvQ8TDGPFobhCm9zVV8Ur
|
||||
30Lh+z68mi6CUkysUfqBvoXj0g52AOWoCjocKGYawK4QhciwZRaLmN2CqNm2e9EX
|
||||
6JUOpdhFCQKBgCbZrNgnncQ7sq/wf+irlpiVU2jVKHoOiky/6L5Ok7WqXUjGHSKs
|
||||
gZGh4JddE4EAQLJpBvmjmHYTikvtH7WZlUvb9gZMmOi+M4TBOz5Mt1mH2+7We2eN
|
||||
Ztu8mDjBUALypac4kXBrDXxBPOvvWGCZ+jyA7MENfkNB9HCQlxU0QfT7AoGATamF
|
||||
F6/0EFvuyU1FgSjJL0t8WJ8sbkv5VO5ei5DfYS/MYT+jrU+u7d/fjtVO9nis8wro
|
||||
ZnT6F8gtO3zoaDYlR5SDhAkyuIRYXfVZID3wi/c1/J/Uba8fC+w8SrOE5KU2GwoT
|
||||
IbsC2xKJ6c0FH5tec4upCwO2vDH7GqCZCTvIkMkCgYEA1KavY6TOdj5V8f6nMiLj
|
||||
PnUuAR2pluzFtpmTrY30DM4IwqA1Nx6awuaLcrhUsoH/uFi+gvr7oItwM7AwF4Al
|
||||
q0YKwDXCDBOO9K5WSirqUsHoALsZxODWeBoxRytca8sfjm/J7WcEAk3UxY729d+H
|
||||
G44/LXMaBEtVltDybw9boPs=
|
||||
-----END PRIVATE KEY-----
|
|
@ -0,0 +1,132 @@
|
|||
use std::{
|
||||
borrow::{Borrow, BorrowMut},
|
||||
process::exit,
|
||||
sync::Arc,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use config::Config;
|
||||
use crypt::Crypt;
|
||||
use tokio::{
|
||||
net::TcpListener,
|
||||
signal::unix::{signal, SignalKind},
|
||||
task,
|
||||
time::sleep,
|
||||
};
|
||||
use userctl::UserCtl;
|
||||
|
||||
use crate::{thrpool::AsyncThreadPool, userctl::OnlineState};
|
||||
|
||||
pub mod config;
|
||||
pub mod crypt;
|
||||
pub mod db;
|
||||
pub mod service;
|
||||
pub mod thrpool;
|
||||
pub mod userctl;
|
||||
|
||||
pub async fn run(config: Config) -> ! {
|
||||
Crypt::init().expect("Failed to load the public key.");
|
||||
|
||||
let userctler = UserCtl::new();
|
||||
let passedinto_ctler = Arc::clone(&userctler);
|
||||
task::spawn(async move {
|
||||
loop {
|
||||
for _ in 0..3600 {
|
||||
sleep(Duration::from_secs(1)).await;
|
||||
{
|
||||
passedinto_ctler
|
||||
.read()
|
||||
.await
|
||||
.borrow_mut()
|
||||
.update_timecounts();
|
||||
}
|
||||
}
|
||||
{
|
||||
passedinto_ctler
|
||||
.write()
|
||||
.await
|
||||
.borrow_mut()
|
||||
.users
|
||||
.write()
|
||||
.unwrap()
|
||||
.retain(|_id, user| user.write().unwrap().state != OnlineState::Unlinked);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let listener = TcpListener::bind(&format!("0.0.0.0:{}", config.port))
|
||||
.await
|
||||
.unwrap();
|
||||
println!("Listening on 0.0.0.0:{}", config.port);
|
||||
|
||||
let mut thread_pool = AsyncThreadPool::new(config.concurrent);
|
||||
|
||||
task::spawn(async move {
|
||||
loop {
|
||||
let (socket, _) = listener.accept().await.unwrap();
|
||||
|
||||
let userctl = Arc::clone(&userctler);
|
||||
|
||||
thread_pool
|
||||
.send(async move {
|
||||
let mut buf = [0u8; 1];
|
||||
socket.readable().await.unwrap();
|
||||
let ip = socket.peer_addr().unwrap();
|
||||
if let Ok(1) = socket.try_read(&mut buf) {
|
||||
match buf[0] {
|
||||
1 => {
|
||||
if let Err(e) =
|
||||
service::prelogin(socket, userctl.read().await.borrow()).await
|
||||
{
|
||||
println!("From {:?} : {}", ip, e.to_string());
|
||||
} else {
|
||||
println!("{:?} logging in.", ip);
|
||||
}
|
||||
}
|
||||
2 => {
|
||||
match service::login(socket, userctl.read().await.borrow()).await {
|
||||
Ok(n) => println!("{:?} login succeded as user {}.", ip, n),
|
||||
Err(e) => println!("From {:?}: {}", ip, e.to_string()),
|
||||
}
|
||||
}
|
||||
3 => {
|
||||
if let Err(e) =
|
||||
service::heartbeat(socket, userctl.read().await.borrow()).await
|
||||
{
|
||||
println!("From {:?} : {}", ip, e.to_string());
|
||||
} else {
|
||||
println!("{:?} heartbeats.", ip);
|
||||
}
|
||||
}
|
||||
4 => {
|
||||
if let Err(e) =
|
||||
service::logout(socket, userctl.read().await.borrow()).await
|
||||
{
|
||||
println!("From {:?} : {}", ip, e.to_string());
|
||||
} else {
|
||||
println!("{:?} logout.", ip);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
};
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
});
|
||||
|
||||
let mut sigterm = signal(SignalKind::terminate()).unwrap();
|
||||
let mut sigint = signal(SignalKind::interrupt()).unwrap();
|
||||
|
||||
tokio::select! {
|
||||
_ = sigint.recv() => {
|
||||
println!("\nReceived SIGINT, exiting...");
|
||||
exit(0);
|
||||
}
|
||||
_ = sigterm.recv() => {
|
||||
println!("Received SIGTERM, exiting...");
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
use accounts::{config::Config, run};
|
||||
|
||||
use clap::Parser;
|
||||
use tokio::io;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> io::Result<()> {
|
||||
let config = Config::parse();
|
||||
run(config).await;
|
||||
Ok(())
|
||||
}
|
|
@ -0,0 +1,217 @@
|
|||
use std::{
|
||||
io::{self, Error, ErrorKind},
|
||||
sync::RwLock,
|
||||
};
|
||||
|
||||
use openssl::hash::{Hasher, MessageDigest};
|
||||
use tokio::net::TcpStream;
|
||||
|
||||
use crate::{
|
||||
crypt::Crypt,
|
||||
db,
|
||||
userctl::{OnlineState, User, UserCtl},
|
||||
};
|
||||
|
||||
async fn receive_decrypt_from(socket: &TcpStream) -> io::Result<String> {
|
||||
let mut buf = [0u8; 1024];
|
||||
let mut secret = Vec::new();
|
||||
loop {
|
||||
socket.readable().await?;
|
||||
let n = socket.try_read(&mut buf)?;
|
||||
secret.append(&mut buf[..n].to_vec());
|
||||
if buf[n - 1] == b'\n' {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(Crypt::decrypt(&secret[..(secret.len() - 1)]))
|
||||
}
|
||||
|
||||
pub async fn prelogin(socket: TcpStream, userctl: &UserCtl) -> io::Result<()> {
|
||||
let text = receive_decrypt_from(&socket).await?;
|
||||
let text = text.split(":").collect::<Vec<&str>>();
|
||||
if text[0] == "southbayhub login" {
|
||||
let id = usize::from_str_radix(text[0], 16).unwrap();
|
||||
if userctl.users.read().unwrap().contains_key(&id)
|
||||
&& userctl
|
||||
.users
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(&id)
|
||||
.unwrap()
|
||||
.read()
|
||||
.unwrap()
|
||||
.state
|
||||
!= OnlineState::Unlinked
|
||||
&& userctl
|
||||
.users
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(&id)
|
||||
.unwrap()
|
||||
.read()
|
||||
.unwrap()
|
||||
.state
|
||||
!= OnlineState::Unavailable
|
||||
{
|
||||
socket.try_write(&Crypt::encrypt("already logged\n"))?;
|
||||
} else {
|
||||
match db::read_user(id).await {
|
||||
Ok(user) => {
|
||||
socket.try_write(&Crypt::encrypt("southbayhub server\n"))?;
|
||||
userctl.users.write().unwrap().insert(id, RwLock::new(user));
|
||||
userctl
|
||||
.state_timecount
|
||||
.write()
|
||||
.unwrap()
|
||||
.insert(id, RwLock::new((0, 10, OnlineState::Unlinked)));
|
||||
}
|
||||
Err(e) => {
|
||||
if let ErrorKind::NotFound = e.kind() {
|
||||
socket.try_write(&Crypt::encrypt("unexist\n"))?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
format!(
|
||||
"Need \"southbayhub login:{{id}}\" but received \"{}\".",
|
||||
text[0]
|
||||
),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// 登录成功返回登录用户的id
|
||||
pub async fn login(socket: TcpStream, userctl: &UserCtl) -> io::Result<usize> {
|
||||
let text = receive_decrypt_from(&socket).await?;
|
||||
let text = text.split(":").collect::<Vec<&str>>();
|
||||
let (id, passwd) = (usize::from_str_radix(text[0], 16).unwrap(), text[1]);
|
||||
if !userctl.users.read().unwrap().contains_key(&id) {
|
||||
socket.try_write(&Crypt::encrypt("unprelogged\n"))?;
|
||||
Err(io::Error::new(
|
||||
ErrorKind::NotFound,
|
||||
format!("{} didn't prelogged.", id),
|
||||
))
|
||||
} else {
|
||||
let guard = userctl.users.read().unwrap();
|
||||
let mut guard = guard.get(&id).unwrap().write().unwrap();
|
||||
let User {
|
||||
passwd: passident,
|
||||
state,
|
||||
..
|
||||
} = &mut *guard;
|
||||
let passident = passident.iter().map(|x| *x as char).collect::<String>();
|
||||
if *state == OnlineState::Logged || *state == OnlineState::Unavailable {
|
||||
socket.try_write(&Crypt::encrypt("logged in\n"))?;
|
||||
Err(io::Error::new(
|
||||
ErrorKind::AlreadyExists,
|
||||
format!("{} had login.", id),
|
||||
))
|
||||
} else if *state == OnlineState::Logging {
|
||||
let sha512str = {
|
||||
let sha512str = passwd
|
||||
.chars()
|
||||
.map(|x| format!("{:x}", x as u8))
|
||||
.collect::<Vec<String>>();
|
||||
let mut ss = String::new();
|
||||
for s in sha512str.into_iter() {
|
||||
ss += &s;
|
||||
}
|
||||
ss
|
||||
};
|
||||
let md5 = {
|
||||
let mut hasher = Hasher::new(MessageDigest::md5())?;
|
||||
hasher.update(sha512str.as_bytes())?;
|
||||
let res = hasher.finish()?;
|
||||
let res = res
|
||||
.iter()
|
||||
.map(|x| format!("{:x}", *x))
|
||||
.collect::<Vec<String>>();
|
||||
let mut ss = String::new();
|
||||
for s in res.into_iter() {
|
||||
ss += &s;
|
||||
}
|
||||
ss
|
||||
};
|
||||
if passident == md5 {
|
||||
*state = OnlineState::Logged;
|
||||
*userctl
|
||||
.state_timecount
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(&id)
|
||||
.unwrap()
|
||||
.write()
|
||||
.unwrap() = (0, 60, OnlineState::Unavailable);
|
||||
socket.try_write(&Crypt::encrypt("ok\n"))?;
|
||||
Ok(id)
|
||||
} else {
|
||||
socket.try_write(&Crypt::encrypt("passwd incorrect\n"))?;
|
||||
Err(io::Error::new(
|
||||
ErrorKind::ConnectionRefused,
|
||||
format!("{}'s passwd incorrect.", id),
|
||||
))
|
||||
}
|
||||
} else {
|
||||
socket.try_write(&Crypt::encrypt("unprelogged\n"))?;
|
||||
Err(io::Error::new(
|
||||
ErrorKind::NotFound,
|
||||
format!("{} didn't prelogged.", id),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn heartbeat(socket: TcpStream, userctl: &UserCtl) -> io::Result<()> {
|
||||
let text = receive_decrypt_from(&socket).await?;
|
||||
let id = usize::from_str_radix(&text, 16).unwrap();
|
||||
if !userctl.users.read().unwrap().contains_key(&id) {
|
||||
return Ok(());
|
||||
} else if {
|
||||
userctl
|
||||
.users
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(&id)
|
||||
.unwrap()
|
||||
.read()
|
||||
.unwrap()
|
||||
.state
|
||||
== OnlineState::Unavailable
|
||||
} {
|
||||
userctl
|
||||
.users
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(&id)
|
||||
.unwrap()
|
||||
.write()
|
||||
.unwrap()
|
||||
.state = OnlineState::Logged;
|
||||
} else {
|
||||
return Ok(());
|
||||
}
|
||||
userctl
|
||||
.state_timecount
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(&id)
|
||||
.unwrap()
|
||||
.write()
|
||||
.unwrap()
|
||||
.0 = 0;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn logout(socket: TcpStream, userctl: &UserCtl) -> io::Result<()> {
|
||||
let text = receive_decrypt_from(&socket).await?;
|
||||
let id = usize::from_str_radix(&text, 16).unwrap();
|
||||
let user = userctl.users.write().unwrap().remove(&id).unwrap();
|
||||
let user = user.read().unwrap();
|
||||
let _ = db::save_user(&*user);
|
||||
let _ = userctl.state_timecount.write().unwrap().remove(&id);
|
||||
Ok(())
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
use std::{future::Future, sync::Arc};
|
||||
|
||||
use tokio::{
|
||||
sync::{
|
||||
mpsc::{self, error::SendError, Sender},
|
||||
Mutex,
|
||||
},
|
||||
task,
|
||||
};
|
||||
|
||||
pub struct AsyncThreadPool<F: Future<Output = ()> + Send> {
|
||||
sender: Sender<F>,
|
||||
}
|
||||
|
||||
impl<F: Future<Output = ()> + Send + 'static> AsyncThreadPool<F> {
|
||||
pub fn new(threads: usize) -> Self {
|
||||
let (sender, receiver) = mpsc::channel::<F>(64);
|
||||
let receiver = Arc::new(Mutex::new(receiver));
|
||||
for _ in 0..threads {
|
||||
let inrc = Arc::clone(&receiver);
|
||||
task::spawn(async move {
|
||||
loop {
|
||||
let f = {
|
||||
let mut f = inrc.lock().await;
|
||||
f.recv().await.unwrap()
|
||||
};
|
||||
f.await;
|
||||
}
|
||||
});
|
||||
}
|
||||
Self { sender }
|
||||
}
|
||||
|
||||
pub async fn send(&mut self, f: F) -> Result<(), SendError<F>> {
|
||||
self.sender.send(f).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
use std::{
|
||||
collections::HashMap,
|
||||
sync::{self, Arc},
|
||||
};
|
||||
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
pub struct UserCtl {
|
||||
pub users: sync::RwLock<HashMap<usize, sync::RwLock<User>>>,
|
||||
pub state_timecount: sync::RwLock<HashMap<usize, sync::RwLock<(usize, usize, OnlineState)>>>,
|
||||
}
|
||||
|
||||
impl UserCtl {
|
||||
pub fn new() -> Arc<RwLock<Self>> {
|
||||
Arc::new(RwLock::new(UserCtl {
|
||||
users: sync::RwLock::new(HashMap::new()),
|
||||
state_timecount: sync::RwLock::new(HashMap::new()),
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn update_timecounts(&self) {
|
||||
for (id, counts) in self.state_timecount.read().unwrap().iter() {
|
||||
{
|
||||
counts.write().unwrap().0 += 1
|
||||
};
|
||||
if counts.read().unwrap().1 != 0 && counts.read().unwrap().0 == counts.read().unwrap().1
|
||||
{
|
||||
self.users.read().unwrap().get(&id).unwrap().write().unwrap().state = counts.read().unwrap().2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct User {
|
||||
pub id: usize,
|
||||
pub name: String,
|
||||
pub passwd: Vec<u8>,
|
||||
pub email: String,
|
||||
pub avatar: u32,
|
||||
|
||||
pub state: OnlineState,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub enum OnlineState {
|
||||
Logging,
|
||||
Logged,
|
||||
Unavailable,
|
||||
Unlinked,
|
||||
}
|
Loading…
Reference in New Issue