updated fully featured text messaging
This commit is contained in:
1 parent
d9d3dd2de9
commit
ded35ae796
4 files changed
+1926
-163
No files matched your search
Generated
+284
-8
@@ -2,6 +2,12 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "adler2"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.8.12"
|
||||
@@ -82,6 +88,7 @@ dependencies = [
|
||||
"matchit",
|
||||
"memchr",
|
||||
"mime",
|
||||
"multer",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"rustversion",
|
||||
@@ -151,6 +158,18 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit_field"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.10.0"
|
||||
@@ -185,6 +204,12 @@ version = "3.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.24.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
@@ -237,6 +262,12 @@ dependencies = [
|
||||
"inout",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "color_quant"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
||||
|
||||
[[package]]
|
||||
name = "const-oid"
|
||||
version = "0.9.6"
|
||||
@@ -283,6 +314,34 @@ version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
|
||||
dependencies = [
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-queue"
|
||||
version = "0.3.12"
|
||||
@@ -298,6 +357,12 @@ version = "0.8.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||
|
||||
[[package]]
|
||||
name = "crunchy"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.7"
|
||||
@@ -363,6 +428,15 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
@@ -396,18 +470,52 @@ version = "2.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
|
||||
|
||||
[[package]]
|
||||
name = "exr"
|
||||
version = "1.74.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4300e043a56aa2cb633c01af81ca8f699a321879a7854d3896a0ba89056363be"
|
||||
dependencies = [
|
||||
"bit_field",
|
||||
"half",
|
||||
"lebe",
|
||||
"miniz_oxide",
|
||||
"rayon-core",
|
||||
"smallvec",
|
||||
"zune-inflate",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
|
||||
[[package]]
|
||||
name = "fdeflate"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c"
|
||||
dependencies = [
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flume"
|
||||
version = "0.11.1"
|
||||
@@ -566,6 +674,27 @@ dependencies = [
|
||||
"wasip2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gif"
|
||||
version = "0.13.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ae047235e33e2829703574b54fdec96bfbad892062d97fed2f76022287de61b"
|
||||
dependencies = [
|
||||
"color_quant",
|
||||
"weezl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "half"
|
||||
version = "2.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crunchy",
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.5"
|
||||
@@ -848,6 +977,24 @@ dependencies = [
|
||||
"icu_properties",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "image"
|
||||
version = "0.24.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"byteorder",
|
||||
"color_quant",
|
||||
"exr",
|
||||
"gif",
|
||||
"jpeg-decoder",
|
||||
"num-traits",
|
||||
"png",
|
||||
"qoi",
|
||||
"tiff",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.12.1"
|
||||
@@ -873,6 +1020,15 @@ version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||
|
||||
[[package]]
|
||||
name = "jpeg-decoder"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00810f1d8b74be64b13dbf3db89ac67740615d6c891f0e7b6179326533011a07"
|
||||
dependencies = [
|
||||
"rayon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.82"
|
||||
@@ -892,6 +1048,12 @@ dependencies = [
|
||||
"spin",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lebe"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.177"
|
||||
@@ -910,7 +1072,7 @@ version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 2.10.0",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
]
|
||||
@@ -997,6 +1159,16 @@ version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "1.1.0"
|
||||
@@ -1016,6 +1188,7 @@ dependencies = [
|
||||
"bcrypt",
|
||||
"chrono",
|
||||
"futures-util",
|
||||
"image",
|
||||
"rand",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -1024,6 +1197,24 @@ dependencies = [
|
||||
"tower-http",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "multer"
|
||||
version = "3.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"encoding_rs",
|
||||
"futures-util",
|
||||
"http",
|
||||
"httparse",
|
||||
"memchr",
|
||||
"mime",
|
||||
"spin",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1120,7 +1311,7 @@ version = "0.10.75"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 2.10.0",
|
||||
"cfg-if",
|
||||
"foreign-types",
|
||||
"libc",
|
||||
@@ -1241,6 +1432,19 @@ version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
||||
|
||||
[[package]]
|
||||
name = "png"
|
||||
version = "0.17.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"crc32fast",
|
||||
"fdeflate",
|
||||
"flate2",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "potential_utf"
|
||||
version = "0.1.4"
|
||||
@@ -1268,6 +1472,15 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "qoi"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.42"
|
||||
@@ -1313,13 +1526,33 @@ dependencies = [
|
||||
"getrandom 0.2.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f"
|
||||
dependencies = [
|
||||
"either",
|
||||
"rayon-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 2.10.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1348,7 +1581,7 @@ version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 2.10.0",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
@@ -1388,7 +1621,7 @@ version = "2.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 2.10.0",
|
||||
"core-foundation",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
@@ -1527,6 +1760,12 @@ dependencies = [
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simd-adler32"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.11"
|
||||
@@ -1679,7 +1918,7 @@ checksum = "1ed31390216d20e538e447a7a9b959e06ed9fc51c37b514b46eb758016ecd418"
|
||||
dependencies = [
|
||||
"atoi",
|
||||
"base64 0.21.7",
|
||||
"bitflags",
|
||||
"bitflags 2.10.0",
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"chrono",
|
||||
@@ -1722,7 +1961,7 @@ checksum = "7c824eb80b894f926f89a0b9da0c7f435d27cdd35b8c655b114e58223918577e"
|
||||
dependencies = [
|
||||
"atoi",
|
||||
"base64 0.21.7",
|
||||
"bitflags",
|
||||
"bitflags 2.10.0",
|
||||
"byteorder",
|
||||
"chrono",
|
||||
"crc",
|
||||
@@ -1881,6 +2120,17 @@ dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tiff"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e"
|
||||
dependencies = [
|
||||
"flate2",
|
||||
"jpeg-decoder",
|
||||
"weezl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinystr"
|
||||
version = "0.8.2"
|
||||
@@ -1992,7 +2242,7 @@ version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 2.10.0",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http",
|
||||
@@ -2179,6 +2429,17 @@ version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.18.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2"
|
||||
dependencies = [
|
||||
"getrandom 0.3.4",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "valuable"
|
||||
version = "0.1.1"
|
||||
@@ -2263,6 +2524,12 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "weezl"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88"
|
||||
|
||||
[[package]]
|
||||
name = "whoami"
|
||||
version = "1.6.1"
|
||||
@@ -2595,3 +2862,12 @@ dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.111",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zune-inflate"
|
||||
version = "0.2.54"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02"
|
||||
dependencies = [
|
||||
"simd-adler32",
|
||||
]
|
||||
+4
-2
@@ -4,7 +4,7 @@ version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
axum = { version = "0.7", features = ["ws", "json"] }
|
||||
axum = { version = "0.7", features = ["ws", "json", "multipart"] }
|
||||
tokio = { version = "1.0", features = ["full"] }
|
||||
tower-http = { version = "0.5", features = ["fs"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
@@ -15,4 +15,6 @@ sqlx = { version = "0.7", features = ["runtime-tokio-native-tls", "sqlite", "mac
|
||||
futures-util = "0.3"
|
||||
bcrypt = "0.15"
|
||||
rand = "0.8"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
uuid = { version = "1.0", features = ["v4"] }
|
||||
image = "0.24"
|
||||
+203
-8
@@ -1,7 +1,7 @@
|
||||
use axum::{
|
||||
extract::{
|
||||
ws::{Message, WebSocket, WebSocketUpgrade},
|
||||
State, Json, Query,
|
||||
State, Json, Query, Multipart,
|
||||
},
|
||||
response::Response,
|
||||
routing::{get, post, delete},
|
||||
@@ -13,6 +13,7 @@ use std::{
|
||||
collections::HashMap,
|
||||
net::SocketAddr,
|
||||
sync::Arc,
|
||||
path::Path,
|
||||
};
|
||||
use tokio::sync::{mpsc, RwLock};
|
||||
use tower_http::services::ServeDir;
|
||||
@@ -21,6 +22,8 @@ use bcrypt::{hash, verify, DEFAULT_COST};
|
||||
use rand::distributions::Alphanumeric;
|
||||
use rand::{thread_rng, Rng};
|
||||
use chrono::{Utc, Duration};
|
||||
use uuid::Uuid;
|
||||
use std::io::Cursor;
|
||||
|
||||
// Types for shared state
|
||||
type UserMap = Arc<RwLock<HashMap<String, mpsc::UnboundedSender<Message>>>>;
|
||||
@@ -72,6 +75,7 @@ struct AuthResponse {
|
||||
struct UserProfile {
|
||||
username: String,
|
||||
phone: String,
|
||||
avatar_url: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
@@ -79,6 +83,7 @@ struct ValidateTokenResponse {
|
||||
success: bool,
|
||||
username: Option<String>,
|
||||
phone: Option<String>,
|
||||
avatar_url: Option<String>,
|
||||
message: Option<String>,
|
||||
}
|
||||
|
||||
@@ -86,6 +91,7 @@ struct ValidateTokenResponse {
|
||||
struct ChatPartner {
|
||||
username: String,
|
||||
phone: String,
|
||||
avatar_url: Option<String>,
|
||||
last_message: Option<String>,
|
||||
last_message_time: Option<i64>,
|
||||
unread_count: i64,
|
||||
@@ -114,11 +120,21 @@ struct DeleteMessageResponse {
|
||||
message: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct UploadAvatarResponse {
|
||||
success: bool,
|
||||
avatar_url: Option<String>,
|
||||
message: String,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Initialize logging
|
||||
tracing_subscriber::fmt::init();
|
||||
|
||||
// Create avatars directory if it doesn't exist
|
||||
tokio::fs::create_dir_all("web/avatars").await?;
|
||||
|
||||
// Initialize database
|
||||
let db_pool = init_db().await?;
|
||||
|
||||
@@ -139,6 +155,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
.route("/api/debug-messages", get(debug_messages_handler))
|
||||
.route("/api/mark-read", post(mark_read_handler))
|
||||
.route("/api/delete-message", post(delete_message_handler))
|
||||
.route("/api/upload-avatar", post(upload_avatar_handler))
|
||||
.route("/api/profile", get(profile_handler))
|
||||
.nest_service("/", ServeDir::new("web"))
|
||||
.with_state(state);
|
||||
@@ -163,6 +180,7 @@ async fn init_db() -> Result<SqlitePool, Box<dyn std::error::Error>> {
|
||||
phone TEXT UNIQUE NOT NULL,
|
||||
username TEXT UNIQUE NOT NULL,
|
||||
password_hash TEXT NOT NULL,
|
||||
avatar_url TEXT,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
"#,
|
||||
@@ -391,7 +409,7 @@ async fn validate_token_handler(
|
||||
Ok(Some(username)) => {
|
||||
// Get user details
|
||||
let user_result = sqlx::query(
|
||||
"SELECT phone FROM users WHERE username = ?"
|
||||
"SELECT phone, avatar_url FROM users WHERE username = ?"
|
||||
)
|
||||
.bind(&username)
|
||||
.fetch_optional(&state.db_pool)
|
||||
@@ -400,10 +418,12 @@ async fn validate_token_handler(
|
||||
match user_result {
|
||||
Ok(Some(row)) => {
|
||||
let phone: String = row.get("phone");
|
||||
let avatar_url: Option<String> = row.get("avatar_url");
|
||||
Json(ValidateTokenResponse {
|
||||
success: true,
|
||||
username: Some(username),
|
||||
phone: Some(phone),
|
||||
avatar_url,
|
||||
message: None,
|
||||
})
|
||||
}
|
||||
@@ -411,12 +431,14 @@ async fn validate_token_handler(
|
||||
success: false,
|
||||
username: None,
|
||||
phone: None,
|
||||
avatar_url: None,
|
||||
message: Some("User not found".to_string()),
|
||||
}),
|
||||
Err(_) => Json(ValidateTokenResponse {
|
||||
success: false,
|
||||
username: None,
|
||||
phone: None,
|
||||
avatar_url: None,
|
||||
message: Some("Database error".to_string()),
|
||||
}),
|
||||
}
|
||||
@@ -425,12 +447,14 @@ async fn validate_token_handler(
|
||||
success: false,
|
||||
username: None,
|
||||
phone: None,
|
||||
avatar_url: None,
|
||||
message: Some("Invalid or expired token".to_string()),
|
||||
}),
|
||||
Err(_) => Json(ValidateTokenResponse {
|
||||
success: false,
|
||||
username: None,
|
||||
phone: None,
|
||||
avatar_url: None,
|
||||
message: Some("Token verification error".to_string()),
|
||||
}),
|
||||
}
|
||||
@@ -615,7 +639,7 @@ async fn get_chats_handler(
|
||||
|
||||
// Получаем информацию о пользователе
|
||||
let user_result = sqlx::query(
|
||||
"SELECT username, phone FROM users WHERE username = ?"
|
||||
"SELECT username, phone, avatar_url FROM users WHERE username = ?"
|
||||
)
|
||||
.bind(&partner_username)
|
||||
.fetch_optional(&state.db_pool)
|
||||
@@ -625,6 +649,7 @@ async fn get_chats_handler(
|
||||
Ok(Some(user_row)) => {
|
||||
let partner_name: String = user_row.get("username");
|
||||
let phone: String = user_row.get("phone");
|
||||
let avatar_url: Option<String> = user_row.get("avatar_url");
|
||||
|
||||
// Получаем последнее сообщение
|
||||
let last_message_result = sqlx::query(
|
||||
@@ -684,6 +709,7 @@ async fn get_chats_handler(
|
||||
let chat = ChatPartner {
|
||||
username: partner_name.clone(),
|
||||
phone,
|
||||
avatar_url,
|
||||
last_message,
|
||||
last_message_time,
|
||||
unread_count,
|
||||
@@ -738,7 +764,7 @@ async fn search_users_handler(
|
||||
|
||||
let users = sqlx::query(
|
||||
r#"
|
||||
SELECT username, phone
|
||||
SELECT username, phone, avatar_url
|
||||
FROM users
|
||||
WHERE (username LIKE ? OR phone LIKE ?)
|
||||
AND username != ?
|
||||
@@ -758,6 +784,7 @@ async fn search_users_handler(
|
||||
results.push(ChatPartner {
|
||||
username: row.get("username"),
|
||||
phone: row.get("phone"),
|
||||
avatar_url: row.get("avatar_url"),
|
||||
last_message: None,
|
||||
last_message_time: None,
|
||||
unread_count: 0,
|
||||
@@ -939,13 +966,181 @@ async fn delete_message_handler(
|
||||
}
|
||||
}
|
||||
|
||||
async fn profile_handler() -> Json<UserProfile> {
|
||||
Json(UserProfile {
|
||||
username: "user".to_string(),
|
||||
phone: "+1234567890".to_string(),
|
||||
// Upload avatar endpoint
|
||||
async fn upload_avatar_handler(
|
||||
State(state): State<Arc<AppState>>,
|
||||
headers: axum::http::HeaderMap,
|
||||
mut multipart: Multipart,
|
||||
) -> Json<UploadAvatarResponse> {
|
||||
// Extract token from header
|
||||
let token = if let Some(auth_header) = headers.get("authorization") {
|
||||
auth_header.to_str().unwrap_or("").replace("Bearer ", "")
|
||||
} else {
|
||||
return Json(UploadAvatarResponse {
|
||||
success: false,
|
||||
avatar_url: None,
|
||||
message: "No token".to_string(),
|
||||
});
|
||||
};
|
||||
|
||||
// Get current user from token
|
||||
let username = match verify_token(&state.db_pool, &token).await {
|
||||
Ok(Some(user)) => user,
|
||||
_ => return Json(UploadAvatarResponse {
|
||||
success: false,
|
||||
avatar_url: None,
|
||||
message: "Invalid token".to_string(),
|
||||
}),
|
||||
};
|
||||
|
||||
// Process multipart form
|
||||
while let Some(field) = multipart.next_field().await.unwrap() {
|
||||
let field_name = field.name().unwrap_or("").to_string();
|
||||
|
||||
if field_name == "avatar" {
|
||||
let data = field.bytes().await.unwrap();
|
||||
|
||||
// Validate image size (max 5MB)
|
||||
if data.len() > 5 * 1024 * 1024 {
|
||||
return Json(UploadAvatarResponse {
|
||||
success: false,
|
||||
avatar_url: None,
|
||||
message: "Image too large (max 5MB)".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
// Process image
|
||||
match process_avatar(&data).await {
|
||||
Ok(processed_data) => {
|
||||
// Generate unique filename
|
||||
let filename = format!("{}.jpg", Uuid::new_v4());
|
||||
let avatar_path = format!("web/avatars/{}", filename);
|
||||
|
||||
// Save file
|
||||
if let Err(e) = tokio::fs::write(&avatar_path, &processed_data).await {
|
||||
eprintln!("Error saving avatar: {}", e);
|
||||
return Json(UploadAvatarResponse {
|
||||
success: false,
|
||||
avatar_url: None,
|
||||
message: "Error saving image".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
let avatar_url = format!("/avatars/{}", filename);
|
||||
|
||||
// Update user in database
|
||||
let result = sqlx::query(
|
||||
"UPDATE users SET avatar_url = ? WHERE username = ?"
|
||||
)
|
||||
.bind(&avatar_url)
|
||||
.bind(&username)
|
||||
.execute(&state.db_pool)
|
||||
.await;
|
||||
|
||||
match result {
|
||||
Ok(_) => {
|
||||
return Json(UploadAvatarResponse {
|
||||
success: true,
|
||||
avatar_url: Some(avatar_url),
|
||||
message: "Avatar uploaded successfully".to_string(),
|
||||
});
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Error updating user avatar: {}", e);
|
||||
return Json(UploadAvatarResponse {
|
||||
success: false,
|
||||
avatar_url: None,
|
||||
message: "Database error".to_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
return Json(UploadAvatarResponse {
|
||||
success: false,
|
||||
avatar_url: None,
|
||||
message: e,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Json(UploadAvatarResponse {
|
||||
success: false,
|
||||
avatar_url: None,
|
||||
message: "No avatar file provided".to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
// Process and resize avatar
|
||||
async fn process_avatar(data: &[u8]) -> Result<Vec<u8>, String> {
|
||||
// Load image
|
||||
let img = match image::load_from_memory(data) {
|
||||
Ok(img) => img,
|
||||
Err(_) => return Err("Invalid image format".to_string()),
|
||||
};
|
||||
|
||||
// Resize image to 150x150 pixels
|
||||
let resized = img.resize_to_fill(150, 150, image::imageops::FilterType::Lanczos3);
|
||||
|
||||
// Convert to JPEG
|
||||
let mut jpeg_data = Vec::new();
|
||||
match resized.write_to(&mut Cursor::new(&mut jpeg_data), image::ImageFormat::Jpeg) {
|
||||
Ok(_) => Ok(jpeg_data),
|
||||
Err(_) => Err("Error processing image".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
async fn profile_handler(
|
||||
State(state): State<Arc<AppState>>,
|
||||
headers: axum::http::HeaderMap,
|
||||
) -> Json<UserProfile> {
|
||||
// Extract token from header
|
||||
let token = if let Some(auth_header) = headers.get("authorization") {
|
||||
auth_header.to_str().unwrap_or("").replace("Bearer ", "")
|
||||
} else {
|
||||
return Json(UserProfile {
|
||||
username: "user".to_string(),
|
||||
phone: "+1234567890".to_string(),
|
||||
avatar_url: None,
|
||||
});
|
||||
};
|
||||
|
||||
// Get current user from token
|
||||
let username = match verify_token(&state.db_pool, &token).await {
|
||||
Ok(Some(user)) => user,
|
||||
_ => return Json(UserProfile {
|
||||
username: "user".to_string(),
|
||||
phone: "+1234567890".to_string(),
|
||||
avatar_url: None,
|
||||
}),
|
||||
};
|
||||
|
||||
// Get user details
|
||||
let user_result = sqlx::query(
|
||||
"SELECT username, phone, avatar_url FROM users WHERE username = ?"
|
||||
)
|
||||
.bind(&username)
|
||||
.fetch_optional(&state.db_pool)
|
||||
.await;
|
||||
|
||||
match user_result {
|
||||
Ok(Some(row)) => {
|
||||
Json(UserProfile {
|
||||
username: row.get("username"),
|
||||
phone: row.get("phone"),
|
||||
avatar_url: row.get("avatar_url"),
|
||||
})
|
||||
}
|
||||
_ => Json(UserProfile {
|
||||
username: "user".to_string(),
|
||||
phone: "+1234567890".to_string(),
|
||||
avatar_url: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
// WebSocket handlers
|
||||
async fn websocket_handler(
|
||||
ws: WebSocketUpgrade,
|
||||
|
||||
+1435
-145
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user