summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTyler Murphy <tylermurphy534@gmail.com>2023-01-31 22:21:19 -0500
committerTyler Murphy <tylermurphy534@gmail.com>2023-01-31 22:21:19 -0500
commitd9125314809e7f03cb155f91d535e94da583a365 (patch)
treef34bc2e978d5e79f0dc62aa7a5faa8f096b46dc5
parentfix admin (diff)
downloadxssbook-d9125314809e7f03cb155f91d535e94da583a365.tar.gz
xssbook-d9125314809e7f03cb155f91d535e94da583a365.tar.bz2
xssbook-d9125314809e7f03cb155f91d535e94da583a365.zip
custosm avatars and banners
-rw-r--r--.gitignore3
-rw-r--r--Cargo.lock311
-rw-r--r--Cargo.toml5
-rw-r--r--README.md10
-rw-r--r--deployments/docker/Dockerfile2
-rw-r--r--deployments/docker/docker-compose.yml1
-rw-r--r--public/css/main.css10
-rw-r--r--public/css/profile.css24
-rw-r--r--public/image/default/0.png (renamed from public/img/0.png)bin7910 -> 7910 bytes
-rw-r--r--public/image/default/1.png (renamed from public/img/1.png)bin5700 -> 5700 bytes
-rw-r--r--public/image/default/10.png (renamed from public/img/10.png)bin8045 -> 8045 bytes
-rw-r--r--public/image/default/11.png (renamed from public/img/11.png)bin6702 -> 6702 bytes
-rw-r--r--public/image/default/12.png (renamed from public/img/12.png)bin6809 -> 6809 bytes
-rw-r--r--public/image/default/13.png (renamed from public/img/13.png)bin7080 -> 7080 bytes
-rw-r--r--public/image/default/14.png (renamed from public/img/14.png)bin5937 -> 5937 bytes
-rw-r--r--public/image/default/15.png (renamed from public/img/15.png)bin6457 -> 6457 bytes
-rw-r--r--public/image/default/16.png (renamed from public/img/16.png)bin7261 -> 7261 bytes
-rw-r--r--public/image/default/17.png (renamed from public/img/17.png)bin5327 -> 5327 bytes
-rw-r--r--public/image/default/18.png (renamed from public/img/18.png)bin7791 -> 7791 bytes
-rw-r--r--public/image/default/19.png (renamed from public/img/19.png)bin7928 -> 7928 bytes
-rw-r--r--public/image/default/2.png (renamed from public/img/2.png)bin7258 -> 7258 bytes
-rw-r--r--public/image/default/20.png (renamed from public/img/20.png)bin8212 -> 8212 bytes
-rw-r--r--public/image/default/21.png (renamed from public/img/21.png)bin6249 -> 6249 bytes
-rw-r--r--public/image/default/22.png (renamed from public/img/22.png)bin6896 -> 6896 bytes
-rw-r--r--public/image/default/23.png (renamed from public/img/23.png)bin7284 -> 7284 bytes
-rw-r--r--public/image/default/24.png (renamed from public/img/24.png)bin6256 -> 6256 bytes
-rw-r--r--public/image/default/3.png (renamed from public/img/3.png)bin7672 -> 7672 bytes
-rw-r--r--public/image/default/4.png (renamed from public/img/4.png)bin7119 -> 7119 bytes
-rw-r--r--public/image/default/5.png (renamed from public/img/5.png)bin6799 -> 6799 bytes
-rw-r--r--public/image/default/6.png (renamed from public/img/6.png)bin5931 -> 5931 bytes
-rw-r--r--public/image/default/7.png (renamed from public/img/7.png)bin6681 -> 6681 bytes
-rw-r--r--public/image/default/8.png (renamed from public/img/8.png)bin6546 -> 6546 bytes
-rw-r--r--public/image/default/9.png (renamed from public/img/9.png)bin7635 -> 7635 bytes
-rw-r--r--public/js/api.js29
-rw-r--r--public/js/main.js6
-rw-r--r--public/js/profile.js27
-rw-r--r--src/api/image.rs54
-rw-r--r--src/api/mod.rs1
-rw-r--r--src/api/users.rs28
-rw-r--r--src/main.rs14
-rw-r--r--src/types/extract.rs74
41 files changed, 576 insertions, 23 deletions
diff --git a/.gitignore b/.gitignore
index bce9d97..8162224 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
/target
-xssbook.db \ No newline at end of file
+xssbook.db
+/public/avatar/custom/* \ No newline at end of file
diff --git a/Cargo.lock b/Cargo.lock
index 9a47937..7b16e84 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3,6 +3,12 @@
version = 3
[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
name = "ahash"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -98,6 +104,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
[[package]]
+name = "bit_field"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4"
+
+[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -119,6 +131,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
[[package]]
+name = "bytemuck"
+version = "1.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c041d3eab048880cb0b86b256447da3f18859a163c3b8d8893f4e6368abe6393"
+
+[[package]]
+name = "byteorder"
+version = "1.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
+
+[[package]]
name = "bytes"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -137,6 +161,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
+name = "color_quant"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
+
+[[package]]
name = "cookie"
version = "0.16.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -157,6 +187,49 @@ dependencies = [
]
[[package]]
+name = "crc32fast"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "crossbeam-channel"
+version = "0.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521"
+dependencies = [
+ "cfg-if",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc"
+dependencies = [
+ "cfg-if",
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a"
+dependencies = [
+ "autocfg",
+ "cfg-if",
+ "crossbeam-utils",
+ "memoffset",
+ "scopeguard",
+]
+
+[[package]]
name = "crossbeam-utils"
version = "0.8.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -166,6 +239,12 @@ dependencies = [
]
[[package]]
+name = "crunchy"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
+
+[[package]]
name = "crypto-common"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -199,6 +278,27 @@ dependencies = [
]
[[package]]
+name = "either"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
+
+[[package]]
+name = "exr"
+version = "1.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8eb5f255b5980bb0c8cf676b675d1a99be40f316881444f44e0462eaf5df5ded"
+dependencies = [
+ "bit_field",
+ "flume",
+ "half",
+ "lebe",
+ "miniz_oxide",
+ "smallvec",
+ "threadpool",
+]
+
+[[package]]
name = "fallible-iterator"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -211,6 +311,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
[[package]]
+name = "flate2"
+version = "1.0.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841"
+dependencies = [
+ "crc32fast",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "flume"
+version = "0.10.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+ "nanorand",
+ "pin-project",
+ "spin",
+]
+
+[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -347,8 +470,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
dependencies = [
"cfg-if",
+ "js-sys",
"libc",
"wasi 0.11.0+wasi-snapshot-preview1",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "gif"
+version = "0.11.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3edd93c6756b4dfaf2709eafcc345ba2636565295c198a9cfbf75fa5e3e00b06"
+dependencies = [
+ "color_quant",
+ "weezl",
]
[[package]]
@@ -370,6 +505,15 @@ dependencies = [
]
[[package]]
+name = "half"
+version = "2.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02b4af3693f1b705df946e9fe5631932443781d0aabb423b62fcd4d73f6d2fd0"
+dependencies = [
+ "crunchy",
+]
+
+[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -485,12 +629,40 @@ dependencies = [
]
[[package]]
+name = "image"
+version = "0.24.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69b7ea949b537b0fd0af141fff8c77690f2ce96f4f41f042ccb6c69c6c965945"
+dependencies = [
+ "bytemuck",
+ "byteorder",
+ "color_quant",
+ "exr",
+ "gif",
+ "jpeg-decoder",
+ "num-rational",
+ "num-traits",
+ "png",
+ "scoped_threadpool",
+ "tiff",
+]
+
+[[package]]
name = "itoa"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
[[package]]
+name = "jpeg-decoder"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e"
+dependencies = [
+ "rayon",
+]
+
+[[package]]
name = "js-sys"
version = "0.3.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -506,6 +678,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
+name = "lebe"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
+
+[[package]]
name = "libc"
version = "0.2.139"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -563,6 +741,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
+name = "memoffset"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
name = "mime"
version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -579,6 +766,15 @@ dependencies = [
]
[[package]]
+name = "miniz_oxide"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa"
+dependencies = [
+ "adler",
+]
+
+[[package]]
name = "mio"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -591,6 +787,15 @@ dependencies = [
]
[[package]]
+name = "nanorand"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
name = "no-std-compat"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -619,6 +824,36 @@ dependencies = [
]
[[package]]
+name = "num-integer"
+version = "0.1.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
+dependencies = [
+ "autocfg",
+ "num-traits",
+]
+
+[[package]]
+name = "num-rational"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
+dependencies = [
+ "autocfg",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
name = "num_cpus"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -708,6 +943,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
[[package]]
+name = "png"
+version = "0.17.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d708eaf860a19b19ce538740d2b4bdeeb8337fa53f7738455e706623ad5c638"
+dependencies = [
+ "bitflags",
+ "crc32fast",
+ "flate2",
+ "miniz_oxide",
+]
+
+[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -787,6 +1034,28 @@ dependencies = [
]
[[package]]
+name = "rayon"
+version = "1.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7"
+dependencies = [
+ "either",
+ "rayon-core",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "356a0625f1954f730c0201cdab48611198dc6ce21f4acff55089b5a78e6e835b"
+dependencies = [
+ "crossbeam-channel",
+ "crossbeam-deque",
+ "crossbeam-utils",
+ "num_cpus",
+]
+
+[[package]]
name = "redox_syscall"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -822,6 +1091,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde"
[[package]]
+name = "scoped_threadpool"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8"
+
+[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -934,6 +1209,15 @@ dependencies = [
]
[[package]]
+name = "spin"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f6002a767bff9e83f8eeecf883ecb8011875a21ae8da43bffb817a57e78cc09"
+dependencies = [
+ "lock_api",
+]
+
+[[package]]
name = "syn"
version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -980,6 +1264,26 @@ dependencies = [
]
[[package]]
+name = "threadpool"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa"
+dependencies = [
+ "num_cpus",
+]
+
+[[package]]
+name = "tiff"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7449334f9ff2baf290d55d73983a7d6fa15e01198faef72af07e2a8db851e471"
+dependencies = [
+ "flate2",
+ "jpeg-decoder",
+ "weezl",
+]
+
+[[package]]
name = "time"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1331,6 +1635,12 @@ dependencies = [
]
[[package]]
+name = "weezl"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb"
+
+[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1416,6 +1726,7 @@ dependencies = [
"axum",
"axum-client-ip",
"bytes",
+ "image",
"lazy_static",
"rand",
"rusqlite",
diff --git a/Cargo.toml b/Cargo.toml
index 9fdbd0c..272112f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -5,7 +5,7 @@ edition = "2021"
[dependencies]
tokio = { version = "1.23.0", features = ["full"] }
-axum = { version = "0.6.4", features = ["headers"] }
+axum = { version = "0.6.4", features = ["headers", "query"] }
axum-client-ip = "0.3.1"
tower-http = { version = "0.3.5", features = ["fs"] }
tower_governor = "0.0.4"
@@ -19,4 +19,5 @@ serde_json = { version = "1.0", features = ["std"] }
rusqlite = { version = "0.28.0", features = ["bundled"] }
rand = "0.8.5"
time = "0.3.17"
-lazy_static = "1.4.0" \ No newline at end of file
+lazy_static = "1.4.0"
+image = "0.24.3" \ No newline at end of file
diff --git a/README.md b/README.md
index 720f5c9..0dfb9d2 100644
--- a/README.md
+++ b/README.md
@@ -30,15 +30,17 @@ If you want to run it in a docker container a premade dockerfile is here for you
There is also a docker-compose.yml file for your reference in the /deployments/docker folder.
-The one thing about the docker container is you have to mount the volume
+There are two volumes you have to make for the container. First one for the database otherwise all data will be wiped upon container restart. You only should volume the database file so create the vollume with the directory below.
+
+`touch [your directory]/xssbook.db`
`-v [your directory]/xssbook.db:/data/xssbook.db`
-to make the database persistant. Finally, before running the container run
+You have to create the database file beforehand because otherwise docker will create a folder there instead, and then the program will crash when it tries to load a folder as a database.
-`touch [your directory]/xssbook.db`
+Finally, you have to make a volume to store custom user avatars and banners. Without this, this data too will be lost upon contaienr restart. To make the volume simply run this with your container.
-since docker will create a folder there otherwise and it won't work.
+`-v [another directory]:/data/public/image/custom`
**reverse proxy**
diff --git a/deployments/docker/Dockerfile b/deployments/docker/Dockerfile
index f14b0a6..9764939 100644
--- a/deployments/docker/Dockerfile
+++ b/deployments/docker/Dockerfile
@@ -13,6 +13,8 @@ COPY --from=builder /usr/local/cargo/bin/xssbook /usr/local/bin/xssbook
RUN mkdir /data
WORKDIR /data
COPY ./public ./public
+RUN mkdir ./public/image/custom
+VOLUME ./public/image/custom
EXPOSE 8080
CMD ["/usr/local/bin/xssbook"] \ No newline at end of file
diff --git a/deployments/docker/docker-compose.yml b/deployments/docker/docker-compose.yml
index 09415e4..1b0b269 100644
--- a/deployments/docker/docker-compose.yml
+++ b/deployments/docker/docker-compose.yml
@@ -10,3 +10,4 @@ services:
- 8080:8080
volumes:
- ${PWD}/xssbook.db:/data/xssbook.db
+ - ${PWD}/custom:/data/public/image/custom
diff --git a/public/css/main.css b/public/css/main.css
index e15ffa6..1926ed9 100644
--- a/public/css/main.css
+++ b/public/css/main.css
@@ -368,4 +368,14 @@ form {
input:focus {
outline: none;
}
+
+ .changeavatar {
+ filter: invert(100%) !important;
+ background-color: #bbbbbb !important;
+ }
+
+ .changebanner {
+ filter: invert(100%) !important;
+ background-color: #bbbbbb !important;
+ }
} \ No newline at end of file
diff --git a/public/css/profile.css b/public/css/profile.css
index 62ce1d2..1077919 100644
--- a/public/css/profile.css
+++ b/public/css/profile.css
@@ -24,7 +24,7 @@ body {
justify-content: center;
}
-#banner div, #banner img {
+#banner .bg, #banner img {
width: 80em;
max-width: 100%;
height: inherit;
@@ -56,6 +56,27 @@ body {
border-radius: 7em;
}
+.changeavatar, .changebanner {
+ all: unset;
+ position: absolute;
+ width: 3em;
+ height: 3em;
+ margin-left: -3em;
+ margin-top: 9em;
+ border-radius: 3em;
+ background-color: var(--secondary);
+ z-index: 10000 !important;
+ text-align: center;
+ background-image: url('data:image/png;base64,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/TwzbKRT88RhYqHQxnIbs6KhEk8TRxRVo3x/xmWF8xZntVxlzXvyF4Zy2soy12kNIYFFLEGEABlVlFCGhSjtGikmUnQe9/APOn6RXDK5SmDkWEAFKiTHD/4Hv2dr5qcm3aRQHOh8se2PESC4CzRqtv19bNuNEyDwDFxpLX+lDsx+kl5raZEjoHcbuLhuafIecLkDDDzpkiE5UoCWP58H3s/om7JA3y3QvebOrXmO0wcgTbNK3gAHh8BogbLXPd7d1T63f3ua8/sBAv9y4GhT1LwAAA14aVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/Pgo8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA0LjQuMC1FeGl2MiI+CiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIKICAgIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiCiAgICB4bWxuczpHSU1QPSJodHRwOi8vd3d3LmdpbXAub3JnL3htcC8iCiAgICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgeG1wTU06RG9jdW1lbnRJRD0iZ2ltcDpkb2NpZDpnaW1wOjNlYWM4M2MyLTUzMjctNDE5ZS1iMjJlLWU2YjE5M2M2ZTc2NiIKICAgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo0MTNkODIxZC1jMDE1LTQ1ZGQtYTAwNS04MTA2OGY3ZTg1YjEiCiAgIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo4ZGRkOGY2My1iODAwLTQ1MDQtYWE3Yi1iNzAwOTRiOTg5YTgiCiAgIEdJTVA6QVBJPSIyLjAiCiAgIEdJTVA6UGxhdGZvcm09IkxpbnV4IgogICBHSU1QOlRpbWVTdGFtcD0iMTY3NTIwNTQzNTkyMzY1NCIKICAgR0lNUDpWZXJzaW9uPSIyLjEwLjMyIgogICBkYzpGb3JtYXQ9ImltYWdlL3BuZyIKICAgdGlmZjpPcmllbnRhdGlvbj0iMSIKICAgeG1wOkNyZWF0b3JUb29sPSJHSU1QIDIuMTAiCiAgIHhtcDpNZXRhZGF0YURhdGU9IjIwMjM6MDE6MzFUMTc6NTA6MzUtMDU6MDAiCiAgIHhtcDpNb2RpZnlEYXRlPSIyMDIzOjAxOjMxVDE3OjUwOjM1LTA1OjAwIj4KICAgPHhtcE1NOkhpc3Rvcnk+CiAgICA8cmRmOlNlcT4KICAgICA8cmRmOmxpCiAgICAgIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiCiAgICAgIHN0RXZ0OmNoYW5nZWQ9Ii8iCiAgICAgIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6ZGJjMjRlYWMtODdmMC00YjJjLWI2MzAtYTBhOTIxY2I1MmJmIgogICAgICBzdEV2dDpzb2Z0d2FyZUFnZW50PSJHaW1wIDIuMTAgKExpbnV4KSIKICAgICAgc3RFdnQ6d2hlbj0iMjAyMy0wMS0zMVQxNzo1MDozNS0wNTowMCIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1NOkhpc3Rvcnk+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgCjw/eHBhY2tldCBlbmQ9InciPz7KJrUFAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAB3RJTUUH5wEfFjIjpTMVIgAAAlFJREFUaN7t2E2ITXEYx/HPHTNehmlMGSXyMiILJBsbid2QHZZsZj8bL2WtMKLYiJWXkhVJsZLUkJeUl0JCTZJB42UwZphmxsIzdZtmxr3m3OPeOt86nc69z/n/n985z/P8n/MnIyMjIyMjI6OimYcL+IWhEh+fcAwzCnUuV6DdTDzAQjxFV4kf2mLMxV2sR19SA++LJ9WW0tufjHMx554kB76FAdSmGMr1GER7IcZVBQ46JXLjR0JOVmMp6sax6cZPTEtSSJJswzs8x0ccKiJXJ8x99CYwzkr04xvO4GXkQesY9r0xd9kJORCOb4nrxhB2Z6JC0g6tmjh/z3N0IHJmQqQt5GKcT2E/rkchuVRpOSLyoS9vFT8f60ZF5cgws7AWi/5iV7CQ6hK/yfpYD0bShZtJTlSqHJmKI+iIkls2FBNaq/Ak4n8AnWgax34JmsspR6qwN9qKbuzApmhtXmHOKPdswOcQvKschDRFYzmEG1iQ99/2aP4eRc4M0xIiO3Ev7j0bYflfhLREy9GH3WP0Ta3haDum43BcP8b8cP50/HY77+2lIqQRl/McWl7gN82HOF8dpfvdGWH2GqvTELIZ72PStnEWtJGcCBFHMWkMm2Z8QU+MXxIhtTgZznRg3T+U++YC7JbhRcyTuJB+PMtLyroSl/yGKAaJr+zVmI2tsZtSaobLsqSF9GMF3pbjXlUxLcpguYooRsib+G7YmKJvDbG+fE0ytA5GtbmCh1EeS0nOn81AMWeirMG1yJWhFI4eHM/lcjUyMjIyMjIyMiqT3wkE0qfVNfiiAAAAAElFTkSuQmCC');
+ cursor: pointer;
+}
+
+.changebanner {
+ position: relative;
+ margin-left: -4em;
+ margin-top: 26em;
+}
+
.infodata {
margin-top: 2em;
display: flex;
@@ -138,5 +159,4 @@ body {
.logout {
flex: 1;
- /* align-self: flex-end !important; */
} \ No newline at end of file
diff --git a/public/img/0.png b/public/image/default/0.png
index 19bbb12..19bbb12 100644
--- a/public/img/0.png
+++ b/public/image/default/0.png
Binary files differ
diff --git a/public/img/1.png b/public/image/default/1.png
index 0466850..0466850 100644
--- a/public/img/1.png
+++ b/public/image/default/1.png
Binary files differ
diff --git a/public/img/10.png b/public/image/default/10.png
index e181798..e181798 100644
--- a/public/img/10.png
+++ b/public/image/default/10.png
Binary files differ
diff --git a/public/img/11.png b/public/image/default/11.png
index 10cc420..10cc420 100644
--- a/public/img/11.png
+++ b/public/image/default/11.png
Binary files differ
diff --git a/public/img/12.png b/public/image/default/12.png
index f62a977..f62a977 100644
--- a/public/img/12.png
+++ b/public/image/default/12.png
Binary files differ
diff --git a/public/img/13.png b/public/image/default/13.png
index c25a5db..c25a5db 100644
--- a/public/img/13.png
+++ b/public/image/default/13.png
Binary files differ
diff --git a/public/img/14.png b/public/image/default/14.png
index b4268e0..b4268e0 100644
--- a/public/img/14.png
+++ b/public/image/default/14.png
Binary files differ
diff --git a/public/img/15.png b/public/image/default/15.png
index 8641079..8641079 100644
--- a/public/img/15.png
+++ b/public/image/default/15.png
Binary files differ
diff --git a/public/img/16.png b/public/image/default/16.png
index 538b8d8..538b8d8 100644
--- a/public/img/16.png
+++ b/public/image/default/16.png
Binary files differ
diff --git a/public/img/17.png b/public/image/default/17.png
index 8fbf42a..8fbf42a 100644
--- a/public/img/17.png
+++ b/public/image/default/17.png
Binary files differ
diff --git a/public/img/18.png b/public/image/default/18.png
index 60ddadc..60ddadc 100644
--- a/public/img/18.png
+++ b/public/image/default/18.png
Binary files differ
diff --git a/public/img/19.png b/public/image/default/19.png
index 87e55ae..87e55ae 100644
--- a/public/img/19.png
+++ b/public/image/default/19.png
Binary files differ
diff --git a/public/img/2.png b/public/image/default/2.png
index e3d3fd2..e3d3fd2 100644
--- a/public/img/2.png
+++ b/public/image/default/2.png
Binary files differ
diff --git a/public/img/20.png b/public/image/default/20.png
index cad21e0..cad21e0 100644
--- a/public/img/20.png
+++ b/public/image/default/20.png
Binary files differ
diff --git a/public/img/21.png b/public/image/default/21.png
index 951295b..951295b 100644
--- a/public/img/21.png
+++ b/public/image/default/21.png
Binary files differ
diff --git a/public/img/22.png b/public/image/default/22.png
index 30a7517..30a7517 100644
--- a/public/img/22.png
+++ b/public/image/default/22.png
Binary files differ
diff --git a/public/img/23.png b/public/image/default/23.png
index 386d550..386d550 100644
--- a/public/img/23.png
+++ b/public/image/default/23.png
Binary files differ
diff --git a/public/img/24.png b/public/image/default/24.png
index fc0ce1a..fc0ce1a 100644
--- a/public/img/24.png
+++ b/public/image/default/24.png
Binary files differ
diff --git a/public/img/3.png b/public/image/default/3.png
index aa2b68a..aa2b68a 100644
--- a/public/img/3.png
+++ b/public/image/default/3.png
Binary files differ
diff --git a/public/img/4.png b/public/image/default/4.png
index a9ee0d0..a9ee0d0 100644
--- a/public/img/4.png
+++ b/public/image/default/4.png
Binary files differ
diff --git a/public/img/5.png b/public/image/default/5.png
index 63e904b..63e904b 100644
--- a/public/img/5.png
+++ b/public/image/default/5.png
Binary files differ
diff --git a/public/img/6.png b/public/image/default/6.png
index 7ba485d..7ba485d 100644
--- a/public/img/6.png
+++ b/public/image/default/6.png
Binary files differ
diff --git a/public/img/7.png b/public/image/default/7.png
index c861dc6..c861dc6 100644
--- a/public/img/7.png
+++ b/public/image/default/7.png
Binary files differ
diff --git a/public/img/8.png b/public/image/default/8.png
index a25833b..a25833b 100644
--- a/public/img/8.png
+++ b/public/image/default/8.png
Binary files differ
diff --git a/public/img/9.png b/public/image/default/9.png
index 70b19fa..70b19fa 100644
--- a/public/img/9.png
+++ b/public/image/default/9.png
Binary files differ
diff --git a/public/js/api.js b/public/js/api.js
index 9845be5..b2ea597 100644
--- a/public/js/api.js
+++ b/public/js/api.js
@@ -1,6 +1,27 @@
const endpoint = '/api'
+const fileRequest = async (url, file, method) => {
+ if (method === undefined) method = 'POST'
+ const response = await fetch(endpoint + url, {
+ method,
+ body: file,
+ headers: {}
+ });
+ if (response.status == 401) {
+ location.href = 'login'
+ }
+ const contentType = response.headers.get("content-type");
+ if (contentType && contentType.indexOf("application/json") !== -1) {
+ const json = await response.json()
+ return { status: response.status, msg: json.msg, json }
+ } else {
+ const msg = await response.text();
+ return { status: response.status, msg }
+ }
+}
+
const request = async (url, body, method) => {
+
if (method === undefined) method = 'POST'
const response = await fetch(endpoint + url, {
method,
@@ -88,4 +109,12 @@ const adminusers = async () => {
const adminsessions = async () => {
return await request('/admin/sessions', {})
+}
+
+const updateavatar = async (file) => {
+ return await fileRequest('/users/avatar', file, 'PUT')
+}
+
+const updatebanner = async (file) => {
+ return await fileRequest('/users/banner', file, 'PUT')
} \ No newline at end of file
diff --git a/public/js/main.js b/public/js/main.js
index 87dd8e0..ffbc1f3 100644
--- a/public/js/main.js
+++ b/public/js/main.js
@@ -33,7 +33,11 @@ function remove(id) {
}
function pfp(id) {
- return `<img src="/img/${id % 25}.png">`
+ return `<img src="/image/avatar?user_id=${id}">`
+}
+
+function banner(id) {
+ return `<img src="/image/banner?user_id=${id}" onerror="this.remove()" >`
}
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
diff --git a/public/js/profile.js b/public/js/profile.js
index 10eb873..90787f0 100644
--- a/public/js/profile.js
+++ b/public/js/profile.js
@@ -16,24 +16,44 @@ function swap(value) {
}
}
+function changeimage(fn) {
+
+ var input = document.createElement('input')
+ input.type = 'file'
+ input.accept= 'image/png'
+
+ input.onchange = async (e) => {
+ var file = e.target.files[0];
+ if (file.type !== 'image/png') {
+ return
+ }
+ let response = await fn(file);
+ alert(response.msg)
+ }
+
+ input.click();
+}
+
function render() {
const html = `
<div id="top">
<div id="banner">
- <div>
-
+ <div class="bg">
+ ${banner(data.user.user_id)}
</div>
+ ${ isself ? `<div class="changebanner" onclick="changeimage(updatebanner)"></div>` : '' }
</div>
<div id="info">
<div class="face">
${pfp(data.user.user_id)}
+ ${ isself ? `<div class="changeavatar" onclick="changeimage(updateavatar)"></div>` : '' }
</div>
<div class="infodata">
<span class="bold ltext">${data.user.firstname + ' ' + data.user.lastname}</span>
<span class="gtext">Joined ${parseDate(new Date(data.user.date))}</span>
</div>
</div>
- <div class="fullline" style="width: 80em; margin-bottom: 0;"></div>
+ <div class="fullline" style="width: 80em; margin-bottom: 0; z-index: 0;"></div>
<div class="profilebuttons">
<button id="profilepostbutton" class="${posts ? 'selected' : ''}" onclick="swap(true)">
Posts
@@ -71,6 +91,7 @@ function render() {
`
append(about)
+
}
async function logout_button() {
diff --git a/src/api/image.rs b/src/api/image.rs
new file mode 100644
index 0000000..84eccc7
--- /dev/null
+++ b/src/api/image.rs
@@ -0,0 +1,54 @@
+use axum::{extract::Query, response::Response, routing::get, Router, http::StatusCode};
+use serde::Deserialize;
+
+use crate::types::http::ResponseCode;
+
+
+
+#[derive(Deserialize)]
+struct AvatarRequest {
+ user_id: u64,
+}
+
+async fn avatar(params: Option<Query<AvatarRequest>>) -> Response {
+
+ let Some(params) = params else {
+ return ResponseCode::BadRequest.text("Missing query paramaters");
+ };
+
+ let custom = format!("/image/custom/avatar/{}.png", params.user_id);
+ let default = format!("/image/default/{}.png", params.user_id % 25);
+
+ let file = ResponseCode::Success.file(&custom).await;
+ if file.status() != StatusCode::OK {
+ return ResponseCode::Success.file(&default).await
+ }
+ file
+}
+
+#[derive(Deserialize)]
+struct BannerRequest {
+ user_id: u64,
+}
+
+async fn banner(params: Option<Query<BannerRequest>>) -> Response {
+
+ let Some(params) = params else {
+ return ResponseCode::BadRequest.text("Missing query paramaters");
+ };
+
+ let custom = format!("/image/custom/banner/{}.png", params.user_id);
+
+ let file = ResponseCode::Success.file(&custom).await;
+ if file.status() != StatusCode::OK {
+ return ResponseCode::NotFound.text("User does not have a custom banner")
+ }
+ file
+}
+
+
+pub fn router() -> Router {
+ Router::new()
+ .route("/avatar", get(avatar))
+ .route("/banner", get(banner))
+}
diff --git a/src/api/mod.rs b/src/api/mod.rs
index d36b127..adc19d7 100644
--- a/src/api/mod.rs
+++ b/src/api/mod.rs
@@ -8,6 +8,7 @@ pub mod auth;
pub mod pages;
pub mod posts;
pub mod users;
+pub mod image;
pub fn router() -> Router {
let governor_conf = Box::new(
diff --git a/src/api/users.rs b/src/api/users.rs
index afcdddd..83a0d4e 100644
--- a/src/api/users.rs
+++ b/src/api/users.rs
@@ -1,9 +1,9 @@
use crate::types::{
- extract::{AuthorizedUser, Check, CheckResult, Json},
+ extract::{AuthorizedUser, Check, CheckResult, Json, Png},
http::ResponseCode,
user::User,
};
-use axum::{response::Response, routing::post, Router};
+use axum::{response::Response, routing::{post, put}, Router};
use serde::Deserialize;
#[derive(Deserialize)]
@@ -63,9 +63,33 @@ async fn load_self(AuthorizedUser(user): AuthorizedUser) -> Response {
ResponseCode::Success.json(&json)
}
+async fn avatar(AuthorizedUser(user): AuthorizedUser, Png(img): Png) -> Response {
+
+ let path = format!("./public/image/custom/avatar/{}.png", user.user_id);
+
+ if img.save(path).is_err() {
+ return ResponseCode::InternalServerError.text("Failed to update avatar");
+ }
+
+ ResponseCode::Success.text("Successfully updated avatar")
+}
+
+async fn banner(AuthorizedUser(user): AuthorizedUser, Png(img): Png) -> Response {
+
+ let path = format!("./public/image/custom/banner/{}.png", user.user_id);
+
+ if img.save(path).is_err() {
+ return ResponseCode::InternalServerError.text("Failed to update banner");
+ }
+
+ ResponseCode::Success.text("Successfully updated banner")
+}
+
pub fn router() -> Router {
Router::new()
.route("/load", post(load_batch))
.route("/self", post(load_self))
.route("/page", post(load_page))
+ .route("/avatar", put(avatar))
+ .route("/banner", put(banner))
}
diff --git a/src/main.rs b/src/main.rs
index a72ec5f..74f0a0b 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -3,10 +3,10 @@ use axum::{
http::{Request, StatusCode},
middleware::{self, Next},
response::Response,
- RequestExt, Router,
+ RequestExt, Router, extract::DefaultBodyLimit,
};
use axum_client_ip::ClientIp;
-use std::{net::SocketAddr, process::exit};
+use std::{net::SocketAddr, process::exit, fs};
use tower_cookies::CookieManagerLayer;
use tracing::{error, info, metadata::LevelFilter};
use tracing_subscriber::{
@@ -14,7 +14,7 @@ use tracing_subscriber::{
};
use types::http::ResponseCode;
-use crate::api::pages;
+use crate::api::{pages, image};
mod admin;
mod api;
@@ -69,13 +69,19 @@ async fn main() {
exit(1)
};
+ fs::create_dir_all("./public/image/custom").expect("Coudn't make custom data directory");
+ fs::create_dir_all("./public/image/custom/avatar").expect("Coudn't make custom avatar directory");
+ fs::create_dir_all("./public/image/custom/banner").expect("Coudn't make custom banner directory");
+
let app = Router::new()
.fallback(not_found)
.layer(middleware::from_fn(log))
.layer(middleware::from_fn(serve))
.nest("/", pages::router())
.nest("/api", api::router())
- .layer(CookieManagerLayer::new());
+ .nest("/image", image::router())
+ .layer(CookieManagerLayer::new())
+ .layer(DefaultBodyLimit::max(512_000));
let Ok(addr) = "[::]:8080".parse::<std::net::SocketAddr>() else {
error!("Failed to parse port binding");
diff --git a/src/types/extract.rs b/src/types/extract.rs
index 50c413b..54f250a 100644
--- a/src/types/extract.rs
+++ b/src/types/extract.rs
@@ -1,4 +1,4 @@
-use std::io::Read;
+use std::io::{Read, Cursor};
use axum::{
async_trait,
@@ -10,6 +10,7 @@ use axum::{
};
use axum_client_ip::ClientIp;
use bytes::Bytes;
+use image::{io::Reader, ImageFormat, DynamicImage};
use serde::de::DeserializeOwned;
use tower_cookies::Cookies;
@@ -99,6 +100,36 @@ where
}
}
+pub struct Png(pub DynamicImage);
+
+#[async_trait]
+impl<S, B> FromRequest<S, B> for Png
+where
+ B: HttpBody + Sync + Send + 'static,
+ B::Data: Send,
+ B::Error: Into<BoxError>,
+ S: Send + Sync,
+{
+ type Rejection = Response;
+
+ async fn from_request(req: Request<B>, state: &S) -> Result<Self> {
+
+ let bytes = match read_body(req, state).await {
+ Ok(body) => body,
+ Err(err) => return Err(err),
+ };
+
+ let mut reader = Reader::new(Cursor::new(bytes));
+ reader.set_format(ImageFormat::Png);
+
+ let Ok(img) = reader.decode() else {
+ return Err(ResponseCode::BadRequest.text("Failed to decode png image"))
+ };
+
+ Ok(Self(img))
+ }
+}
+
pub struct Json<T>(pub T);
#[async_trait]
@@ -150,7 +181,43 @@ pub trait Check {
}
}
-pub async fn parse_body<S, B>(mut req: Request<B>, state: &S) -> Result<String>
+async fn read_body<S, B>(mut req: Request<B>, state: &S) -> Result<Vec<u8>>
+where
+ B: HttpBody + Sync + Send + 'static,
+ B::Data: Send,
+ B::Error: Into<BoxError>,
+ S: Send + Sync,
+{
+
+ let Ok(ClientIp(ip)) = req.extract_parts::<ClientIp>().await else {
+ tracing::error!("Failed to read client ip");
+ return Err(ResponseCode::InternalServerError.text("Failed to read client ip"));
+ };
+
+ let method = req.method().clone();
+ let uri = req.uri().clone();
+ let path = req
+ .extensions()
+ .get::<RouterURI>()
+ .map_or("", |path| path.0);
+
+ let Ok(bytes) = Bytes::from_request(req, state).await else {
+ return Err(ResponseCode::BadRequest.text("Request can be at most 512kb"));
+ };
+
+ console::log(
+ ip,
+ method,
+ uri,
+ Some(path.to_string()),
+ None,
+ )
+ .await;
+
+ Ok(bytes.bytes().flatten().collect())
+}
+
+async fn parse_body<S, B>(mut req: Request<B>, state: &S) -> Result<String>
where
B: HttpBody + Sync + Send + 'static,
B::Data: Send,
@@ -170,8 +237,7 @@ where
.map_or("", |path| path.0);
let Ok(bytes) = Bytes::from_request(req, state).await else {
- tracing::error!("Failed to read request body");
- return Err(ResponseCode::InternalServerError.text("Failed to read request body"));
+ return Err(ResponseCode::BadRequest.text("Request can be at most 512kb"));
};
let Ok(body) = String::from_utf8(bytes.bytes().flatten().collect()) else {