this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Gleam frontend to get up to date with typescript one. (#47)

* I think this list is pretty much complete?

* Fixed some lingual errors

* Sync the page definitions w the typescript version

* Ignore that line (bun runs the tests, so it not existing is solved)

* Remove this comment, as the decoder has already been written.

* Upgrade ffi from js to ts

* Re-enable those sourcemaps, even though they're inspecific.

But I need to know stuff, and any is better than none.

* Link prelude for gleam ffi dev

* work on the FEJSON-functions

* Order

* We are making progress

* rename generic ffi module to specific ffi module

* Add element actions module

* Block comment for copyright header

* And its usage

* THROW THOSE

* Bruh, gleam compiler does not recognise mts

* Fix package json

* Had to debug the backend for sending/receiving credentials

* Now able to send credentials to fe api

* SUUUREE... https is the default... ugh

* Always return errorvalue

* Seem to have finished the login module

* Update todo

* Convert formdata.encode to edit a request by itself

* Add mobile nav enhancements to todo

* Format

* Add @types/bun

* consolidating node_modules patterns

* Remove io import

* Improve the session handling function name and structure

* add a module header

* let bun do the symlinking

* Don't let that error slow you down.

* eww get out!

* Move nodemon config to nodemon.json

* Implement queue limit to FEJson fn queue

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Got this mainly usable

* Update todo

* Remove lousy space

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Slow down fejson fns

* Just commenting this in so I can go to sleep knowing I won't mess up tomorrow

* Update gleamy_lights (#50) (#51)

* Update gleamy_lights

* Update manifest.toml

* Expand element actions with hiding and viewing

* Woo, mobile menu'ing

* Why the heck?

* Uh?

* Register some FEJsonFN's

* I think the next thing I do will be in this file

* Minor spelling mistake

* Turn those to shareds

* For today

* taking out the minify until pull

* I am not finished but battery power threathens me

* update the dc

* update the dc

* ...

* Add Rust backend back in

* Makin' it work, kinda

* Allow any subcommand

* switch to RC

* Always return errorvalue, even if empty

* fmt

* catch both

* Switch to JSON encoding

* fmt

* fix dat

* And that required pprint!

* slowly putting lustre into the frontend where it probably should have been all along

* Add mod comment to media

* do Array to List conversion in Gleam instead of JS

* Getting some communication errors out of the way

* Hey svg

* enable username check

* Updat

* Move tailwind config to root

* Adding this.

* Re-enable minification for gleam FE

* Add rendering module

First effort to move things like templating to the front end, where it
should have been!

* Commeeeeeents

* Anticipating the sourcemap PR

* Give `fence()` an actual doc comment, cuz even I got kind of mesmerized

* update `colors`

* Keep the preset placeholder away

* Yup, the actual migration to Lustre on FE started

* Muffle the warnings. Hear no sounds.

* Working on the original PR goals ooh!

* Little list

* thats GLEAM

* Guess what, now we're up to date with main!

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

authored by

Mar
coderabbitai[bot]
and committed by
GitHub
b07a0c0e 453cf4ea

+11641 -193
+28 -6
.devcontainer/devcontainer.json
··· 1 1 { 2 - "image": "mcr.microsoft.com/devcontainers/universal:2", 3 - "features": { 4 - "ghcr.io/devcontainers-extra/features/gleam:1": {}, 5 - "ghcr.io/prulloac/devcontainer-features/bun:1": {}, 6 - "ghcr.io/itsmechlark/features/postgresql:1": {} 7 - } 2 + "image": "mcr.microsoft.com/devcontainers/universal:2", 3 + "features": { 4 + "ghcr.io/devcontainers-extra/features/gleam:1": { 5 + "version": "v1.7.0-rc2" 6 + }, 7 + "ghcr.io/devcontainers/features/rust:1": { 8 + "version": "latest" 9 + }, 10 + "ghcr.io/itsmechlark/features/postgresql:1": { 11 + "version": "latest" 12 + }, 13 + "ghcr.io/lumenpink/devcontainer-features/bun:0": {}, 14 + "ghcr.io/devcontainers-extra/features/asdf-package:1": { 15 + "plugin": "erlang", 16 + "version": "latest", 17 + "pluginRepo": "https://github.com/asdf-vm/asdf-erlang.git" 18 + } 19 + }, 20 + "customizations": { 21 + "vscode": { 22 + "extensions": [ 23 + "gleam.gleam", 24 + "ms-ossdata.vscode-postgresql", 25 + "rust-lang.rust-analyzer", 26 + "Yasuo-Higano.gleam-qol" 27 + ] 28 + } 29 + } 8 30 }
+4
.gitattributes
··· 1 + 2 + * text=auto 3 + *.svg linguist-detectable 4 + *_ffi.ts linguist-language=Gleam
+26 -25
.github/workflows/test.yml
··· 1 1 name: tests 2 2 on: 3 - push: 4 - branches: 5 - - master 6 - - main 7 - pull_request: 3 + push: 4 + branches: 5 + - master 6 + - main 7 + pull_request: 8 8 9 9 jobs: 10 - test: 11 - runs-on: ubuntu-latest 12 - steps: 13 - - uses: actions/checkout@v4 14 - - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af #@v1 15 - with: 16 - profile: minimal 17 - toolchain: stable 18 - components: clippy 19 - override: true 20 - - uses: erlef/setup-beam@v1 21 - with: 22 - otp-version: "26.0.2" 23 - gleam-version: "1.4.1" 24 - rebar3-version: "3" 25 - # elixir-version: "1.15.4" 26 - - uses: oven-sh/setup-bun@v2 27 - with: 28 - bun-version: "1.1.30" 29 - - run: bash ./build.sh --test 10 + test: 11 + runs-on: ubuntu-latest 12 + steps: 13 + - uses: actions/checkout@v4 14 + - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af #@v1 15 + with: 16 + profile: minimal 17 + toolchain: stable 18 + components: clippy 19 + override: true 20 + - uses: erlef/setup-beam@v1 21 + with: 22 + otp-version: "26.0.2" 23 + gleam-version: "1.4.1" 24 + rebar3-version: "3" 25 + # elixir-version: "1.15.4" 26 + - uses: oven-sh/setup-bun@v2 27 + with: 28 + bun-version: "1.1.30" 29 + # For now at least. 30 + - run: bash ./build.sh --test --backend=gleam
+12 -4
.gitignore
··· 1 1 *.beam 2 2 *.ez 3 3 /target/ 4 + 5 + # Ignore Gleam backend build artifacts 4 6 backend/build 5 7 backend/priv/generated 8 + 9 + # Ignore Rust backend build artifacts 10 + backend-rs/target 11 + backend-rs/generated 12 + 13 + # Ignore Gleam frontend build artifacts 14 + frontend/prelude.mjs 15 + 6 16 erl_crash.dump 7 17 /test 8 18 *.log 9 - frontend-ts/node_modules/ 10 - frontend/node_modules/ 11 - backend/node_modules/ 19 + node_modules 12 20 package-lock.json 13 21 rsffi/target 14 22 rsffi/test 23 + node_modules 15 24 # Ignore Editor files 16 25 .idea/ 17 26 .vscode/ 18 27 19 - node_modules
+10
README.MD
··· 1 1 # Lumina(/peonies) server 2 2 3 + ## Currently behind: 4 + As Lumina is a monorepo with pluggable parts, some of them sometimes lag behind and are broken by the latest changes. The current state of the project is: 5 + ### Up to date 6 + - Server (Rust) 7 + 8 + ### behind 9 + - Server (Gleam) (changes in Gleam client) 10 + - Client (TypeScript) (changes in Rust server) 11 + - Client (Gleam) (in progress) 12 + 3 13 ## Requirements 4 14 - [The Gleam compiler, `Erlang` and `rebar3`](https://gleam.run/getting-started/installing/) 5 15 - A PostGres database (or a SQLite one if you want to be QD!)
+3665
backend-rs/Cargo.lock
··· 1 + # This file is automatically @generated by Cargo. 2 + # It is not intended for manual editing. 3 + version = 4 4 + 5 + [[package]] 6 + name = "actix-codec" 7 + version = "0.5.2" 8 + source = "registry+https://github.com/rust-lang/crates.io-index" 9 + checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a" 10 + dependencies = [ 11 + "bitflags 2.6.0", 12 + "bytes", 13 + "futures-core", 14 + "futures-sink", 15 + "memchr", 16 + "pin-project-lite", 17 + "tokio", 18 + "tokio-util", 19 + "tracing", 20 + ] 21 + 22 + [[package]] 23 + name = "actix-http" 24 + version = "3.8.0" 25 + source = "registry+https://github.com/rust-lang/crates.io-index" 26 + checksum = "3ae682f693a9cd7b058f2b0b5d9a6d7728a8555779bedbbc35dd88528611d020" 27 + dependencies = [ 28 + "actix-codec", 29 + "actix-rt", 30 + "actix-service", 31 + "actix-utils", 32 + "ahash", 33 + "base64 0.22.1", 34 + "bitflags 2.6.0", 35 + "brotli", 36 + "bytes", 37 + "bytestring", 38 + "derive_more 0.99.18", 39 + "encoding_rs", 40 + "flate2", 41 + "futures-core", 42 + "h2 0.3.26", 43 + "http 0.2.12", 44 + "httparse", 45 + "httpdate", 46 + "itoa", 47 + "language-tags", 48 + "local-channel", 49 + "mime", 50 + "percent-encoding", 51 + "pin-project-lite", 52 + "rand", 53 + "sha1", 54 + "smallvec", 55 + "tokio", 56 + "tokio-util", 57 + "tracing", 58 + "zstd", 59 + ] 60 + 61 + [[package]] 62 + name = "actix-identity" 63 + version = "0.8.0" 64 + source = "registry+https://github.com/rust-lang/crates.io-index" 65 + checksum = "23b8ddc6f6a8b19c4016aaa13519968da9969bc3bc1c1c883cdb0f25dd6c8cf7" 66 + dependencies = [ 67 + "actix-service", 68 + "actix-session", 69 + "actix-utils", 70 + "actix-web", 71 + "derive_more 1.0.0", 72 + "futures-core", 73 + "serde", 74 + "tracing", 75 + ] 76 + 77 + [[package]] 78 + name = "actix-macros" 79 + version = "0.2.4" 80 + source = "registry+https://github.com/rust-lang/crates.io-index" 81 + checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" 82 + dependencies = [ 83 + "quote", 84 + "syn 2.0.68", 85 + ] 86 + 87 + [[package]] 88 + name = "actix-router" 89 + version = "0.5.3" 90 + source = "registry+https://github.com/rust-lang/crates.io-index" 91 + checksum = "13d324164c51f63867b57e73ba5936ea151b8a41a1d23d1031eeb9f70d0236f8" 92 + dependencies = [ 93 + "bytestring", 94 + "cfg-if", 95 + "http 0.2.12", 96 + "regex", 97 + "regex-lite", 98 + "serde", 99 + "tracing", 100 + ] 101 + 102 + [[package]] 103 + name = "actix-rt" 104 + version = "2.10.0" 105 + source = "registry+https://github.com/rust-lang/crates.io-index" 106 + checksum = "24eda4e2a6e042aa4e55ac438a2ae052d3b5da0ecf83d7411e1a368946925208" 107 + dependencies = [ 108 + "futures-core", 109 + "tokio", 110 + ] 111 + 112 + [[package]] 113 + name = "actix-server" 114 + version = "2.4.0" 115 + source = "registry+https://github.com/rust-lang/crates.io-index" 116 + checksum = "b02303ce8d4e8be5b855af6cf3c3a08f3eff26880faad82bab679c22d3650cb5" 117 + dependencies = [ 118 + "actix-rt", 119 + "actix-service", 120 + "actix-utils", 121 + "futures-core", 122 + "futures-util", 123 + "mio", 124 + "socket2 0.5.7", 125 + "tokio", 126 + "tracing", 127 + ] 128 + 129 + [[package]] 130 + name = "actix-service" 131 + version = "2.0.2" 132 + source = "registry+https://github.com/rust-lang/crates.io-index" 133 + checksum = "3b894941f818cfdc7ccc4b9e60fa7e53b5042a2e8567270f9147d5591893373a" 134 + dependencies = [ 135 + "futures-core", 136 + "paste", 137 + "pin-project-lite", 138 + ] 139 + 140 + [[package]] 141 + name = "actix-session" 142 + version = "0.10.1" 143 + source = "registry+https://github.com/rust-lang/crates.io-index" 144 + checksum = "efe6976a74f34f1b6d07a6c05aadc0ed0359304a7781c367fa5b4029418db08f" 145 + dependencies = [ 146 + "actix-service", 147 + "actix-utils", 148 + "actix-web", 149 + "anyhow", 150 + "derive_more 1.0.0", 151 + "rand", 152 + "serde", 153 + "serde_json", 154 + "tracing", 155 + ] 156 + 157 + [[package]] 158 + name = "actix-utils" 159 + version = "3.0.1" 160 + source = "registry+https://github.com/rust-lang/crates.io-index" 161 + checksum = "88a1dcdff1466e3c2488e1cb5c36a71822750ad43839937f85d2f4d9f8b705d8" 162 + dependencies = [ 163 + "local-waker", 164 + "pin-project-lite", 165 + ] 166 + 167 + [[package]] 168 + name = "actix-web" 169 + version = "4.8.0" 170 + source = "registry+https://github.com/rust-lang/crates.io-index" 171 + checksum = "1988c02af8d2b718c05bc4aeb6a66395b7cdf32858c2c71131e5637a8c05a9ff" 172 + dependencies = [ 173 + "actix-codec", 174 + "actix-http", 175 + "actix-macros", 176 + "actix-router", 177 + "actix-rt", 178 + "actix-server", 179 + "actix-service", 180 + "actix-utils", 181 + "actix-web-codegen", 182 + "ahash", 183 + "bytes", 184 + "bytestring", 185 + "cfg-if", 186 + "cookie", 187 + "derive_more 0.99.18", 188 + "encoding_rs", 189 + "futures-core", 190 + "futures-util", 191 + "itoa", 192 + "language-tags", 193 + "log", 194 + "mime", 195 + "once_cell", 196 + "pin-project-lite", 197 + "regex", 198 + "regex-lite", 199 + "serde", 200 + "serde_json", 201 + "serde_urlencoded", 202 + "smallvec", 203 + "socket2 0.5.7", 204 + "time", 205 + "url", 206 + ] 207 + 208 + [[package]] 209 + name = "actix-web-codegen" 210 + version = "4.3.0" 211 + source = "registry+https://github.com/rust-lang/crates.io-index" 212 + checksum = "f591380e2e68490b5dfaf1dd1aa0ebe78d84ba7067078512b4ea6e4492d622b8" 213 + dependencies = [ 214 + "actix-router", 215 + "proc-macro2", 216 + "quote", 217 + "syn 2.0.68", 218 + ] 219 + 220 + [[package]] 221 + name = "addr2line" 222 + version = "0.22.0" 223 + source = "registry+https://github.com/rust-lang/crates.io-index" 224 + checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" 225 + dependencies = [ 226 + "gimli", 227 + ] 228 + 229 + [[package]] 230 + name = "adler" 231 + version = "1.0.2" 232 + source = "registry+https://github.com/rust-lang/crates.io-index" 233 + checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 234 + 235 + [[package]] 236 + name = "aead" 237 + version = "0.5.2" 238 + source = "registry+https://github.com/rust-lang/crates.io-index" 239 + checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" 240 + dependencies = [ 241 + "crypto-common", 242 + "generic-array", 243 + ] 244 + 245 + [[package]] 246 + name = "aes" 247 + version = "0.8.4" 248 + source = "registry+https://github.com/rust-lang/crates.io-index" 249 + checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" 250 + dependencies = [ 251 + "cfg-if", 252 + "cipher", 253 + "cpufeatures", 254 + ] 255 + 256 + [[package]] 257 + name = "aes-gcm" 258 + version = "0.10.3" 259 + source = "registry+https://github.com/rust-lang/crates.io-index" 260 + checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" 261 + dependencies = [ 262 + "aead", 263 + "aes", 264 + "cipher", 265 + "ctr", 266 + "ghash", 267 + "subtle", 268 + ] 269 + 270 + [[package]] 271 + name = "ahash" 272 + version = "0.8.11" 273 + source = "registry+https://github.com/rust-lang/crates.io-index" 274 + checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" 275 + dependencies = [ 276 + "cfg-if", 277 + "getrandom", 278 + "once_cell", 279 + "version_check", 280 + "zerocopy", 281 + ] 282 + 283 + [[package]] 284 + name = "aho-corasick" 285 + version = "1.1.3" 286 + source = "registry+https://github.com/rust-lang/crates.io-index" 287 + checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 288 + dependencies = [ 289 + "memchr", 290 + ] 291 + 292 + [[package]] 293 + name = "alloc-no-stdlib" 294 + version = "2.0.4" 295 + source = "registry+https://github.com/rust-lang/crates.io-index" 296 + checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" 297 + 298 + [[package]] 299 + name = "alloc-stdlib" 300 + version = "0.2.2" 301 + source = "registry+https://github.com/rust-lang/crates.io-index" 302 + checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" 303 + dependencies = [ 304 + "alloc-no-stdlib", 305 + ] 306 + 307 + [[package]] 308 + name = "android-tzdata" 309 + version = "0.1.1" 310 + source = "registry+https://github.com/rust-lang/crates.io-index" 311 + checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" 312 + 313 + [[package]] 314 + name = "android_system_properties" 315 + version = "0.1.5" 316 + source = "registry+https://github.com/rust-lang/crates.io-index" 317 + checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 318 + dependencies = [ 319 + "libc", 320 + ] 321 + 322 + [[package]] 323 + name = "anyhow" 324 + version = "1.0.86" 325 + source = "registry+https://github.com/rust-lang/crates.io-index" 326 + checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" 327 + 328 + [[package]] 329 + name = "async-channel" 330 + version = "1.9.0" 331 + source = "registry+https://github.com/rust-lang/crates.io-index" 332 + checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" 333 + dependencies = [ 334 + "concurrent-queue", 335 + "event-listener 2.5.3", 336 + "futures-core", 337 + ] 338 + 339 + [[package]] 340 + name = "async-channel" 341 + version = "2.3.1" 342 + source = "registry+https://github.com/rust-lang/crates.io-index" 343 + checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" 344 + dependencies = [ 345 + "concurrent-queue", 346 + "event-listener-strategy", 347 + "futures-core", 348 + "pin-project-lite", 349 + ] 350 + 351 + [[package]] 352 + name = "async-executor" 353 + version = "1.12.0" 354 + source = "registry+https://github.com/rust-lang/crates.io-index" 355 + checksum = "c8828ec6e544c02b0d6691d21ed9f9218d0384a82542855073c2a3f58304aaf0" 356 + dependencies = [ 357 + "async-task", 358 + "concurrent-queue", 359 + "fastrand 2.1.0", 360 + "futures-lite 2.3.0", 361 + "slab", 362 + ] 363 + 364 + [[package]] 365 + name = "async-global-executor" 366 + version = "2.4.1" 367 + source = "registry+https://github.com/rust-lang/crates.io-index" 368 + checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" 369 + dependencies = [ 370 + "async-channel 2.3.1", 371 + "async-executor", 372 + "async-io 2.3.3", 373 + "async-lock 3.4.0", 374 + "blocking", 375 + "futures-lite 2.3.0", 376 + "once_cell", 377 + ] 378 + 379 + [[package]] 380 + name = "async-io" 381 + version = "1.13.0" 382 + source = "registry+https://github.com/rust-lang/crates.io-index" 383 + checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" 384 + dependencies = [ 385 + "async-lock 2.8.0", 386 + "autocfg", 387 + "cfg-if", 388 + "concurrent-queue", 389 + "futures-lite 1.13.0", 390 + "log", 391 + "parking", 392 + "polling 2.8.0", 393 + "rustix 0.37.27", 394 + "slab", 395 + "socket2 0.4.10", 396 + "waker-fn", 397 + ] 398 + 399 + [[package]] 400 + name = "async-io" 401 + version = "2.3.3" 402 + source = "registry+https://github.com/rust-lang/crates.io-index" 403 + checksum = "0d6baa8f0178795da0e71bc42c9e5d13261aac7ee549853162e66a241ba17964" 404 + dependencies = [ 405 + "async-lock 3.4.0", 406 + "cfg-if", 407 + "concurrent-queue", 408 + "futures-io", 409 + "futures-lite 2.3.0", 410 + "parking", 411 + "polling 3.7.2", 412 + "rustix 0.38.34", 413 + "slab", 414 + "tracing", 415 + "windows-sys 0.52.0", 416 + ] 417 + 418 + [[package]] 419 + name = "async-lock" 420 + version = "2.8.0" 421 + source = "registry+https://github.com/rust-lang/crates.io-index" 422 + checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" 423 + dependencies = [ 424 + "event-listener 2.5.3", 425 + ] 426 + 427 + [[package]] 428 + name = "async-lock" 429 + version = "3.4.0" 430 + source = "registry+https://github.com/rust-lang/crates.io-index" 431 + checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" 432 + dependencies = [ 433 + "event-listener 5.3.1", 434 + "event-listener-strategy", 435 + "pin-project-lite", 436 + ] 437 + 438 + [[package]] 439 + name = "async-std" 440 + version = "1.12.0" 441 + source = "registry+https://github.com/rust-lang/crates.io-index" 442 + checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" 443 + dependencies = [ 444 + "async-channel 1.9.0", 445 + "async-global-executor", 446 + "async-io 1.13.0", 447 + "async-lock 2.8.0", 448 + "crossbeam-utils", 449 + "futures-channel", 450 + "futures-core", 451 + "futures-io", 452 + "futures-lite 1.13.0", 453 + "gloo-timers", 454 + "kv-log-macro", 455 + "log", 456 + "memchr", 457 + "once_cell", 458 + "pin-project-lite", 459 + "pin-utils", 460 + "slab", 461 + "wasm-bindgen-futures", 462 + ] 463 + 464 + [[package]] 465 + name = "async-task" 466 + version = "4.7.1" 467 + source = "registry+https://github.com/rust-lang/crates.io-index" 468 + checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" 469 + 470 + [[package]] 471 + name = "async-trait" 472 + version = "0.1.83" 473 + source = "registry+https://github.com/rust-lang/crates.io-index" 474 + checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" 475 + dependencies = [ 476 + "proc-macro2", 477 + "quote", 478 + "syn 2.0.68", 479 + ] 480 + 481 + [[package]] 482 + name = "atomic-waker" 483 + version = "1.1.2" 484 + source = "registry+https://github.com/rust-lang/crates.io-index" 485 + checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" 486 + 487 + [[package]] 488 + name = "autocfg" 489 + version = "1.3.0" 490 + source = "registry+https://github.com/rust-lang/crates.io-index" 491 + checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" 492 + 493 + [[package]] 494 + name = "backtrace" 495 + version = "0.3.73" 496 + source = "registry+https://github.com/rust-lang/crates.io-index" 497 + checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" 498 + dependencies = [ 499 + "addr2line", 500 + "cc", 501 + "cfg-if", 502 + "libc", 503 + "miniz_oxide", 504 + "object", 505 + "rustc-demangle", 506 + ] 507 + 508 + [[package]] 509 + name = "base64" 510 + version = "0.20.0" 511 + source = "registry+https://github.com/rust-lang/crates.io-index" 512 + checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5" 513 + 514 + [[package]] 515 + name = "base64" 516 + version = "0.22.1" 517 + source = "registry+https://github.com/rust-lang/crates.io-index" 518 + checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" 519 + 520 + [[package]] 521 + name = "base64ct" 522 + version = "1.6.0" 523 + source = "registry+https://github.com/rust-lang/crates.io-index" 524 + checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" 525 + 526 + [[package]] 527 + name = "bitflags" 528 + version = "1.3.2" 529 + source = "registry+https://github.com/rust-lang/crates.io-index" 530 + checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 531 + 532 + [[package]] 533 + name = "bitflags" 534 + version = "2.6.0" 535 + source = "registry+https://github.com/rust-lang/crates.io-index" 536 + checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" 537 + 538 + [[package]] 539 + name = "block-buffer" 540 + version = "0.10.4" 541 + source = "registry+https://github.com/rust-lang/crates.io-index" 542 + checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" 543 + dependencies = [ 544 + "generic-array", 545 + ] 546 + 547 + [[package]] 548 + name = "block-padding" 549 + version = "0.3.3" 550 + source = "registry+https://github.com/rust-lang/crates.io-index" 551 + checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" 552 + dependencies = [ 553 + "generic-array", 554 + ] 555 + 556 + [[package]] 557 + name = "blocking" 558 + version = "1.6.1" 559 + source = "registry+https://github.com/rust-lang/crates.io-index" 560 + checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" 561 + dependencies = [ 562 + "async-channel 2.3.1", 563 + "async-task", 564 + "futures-io", 565 + "futures-lite 2.3.0", 566 + "piper", 567 + ] 568 + 569 + [[package]] 570 + name = "brotli" 571 + version = "6.0.0" 572 + source = "registry+https://github.com/rust-lang/crates.io-index" 573 + checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b" 574 + dependencies = [ 575 + "alloc-no-stdlib", 576 + "alloc-stdlib", 577 + "brotli-decompressor", 578 + ] 579 + 580 + [[package]] 581 + name = "brotli-decompressor" 582 + version = "4.0.1" 583 + source = "registry+https://github.com/rust-lang/crates.io-index" 584 + checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" 585 + dependencies = [ 586 + "alloc-no-stdlib", 587 + "alloc-stdlib", 588 + ] 589 + 590 + [[package]] 591 + name = "build_const" 592 + version = "0.2.2" 593 + source = "registry+https://github.com/rust-lang/crates.io-index" 594 + checksum = "b4ae4235e6dac0694637c763029ecea1a2ec9e4e06ec2729bd21ba4d9c863eb7" 595 + 596 + [[package]] 597 + name = "bumpalo" 598 + version = "3.16.0" 599 + source = "registry+https://github.com/rust-lang/crates.io-index" 600 + checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" 601 + 602 + [[package]] 603 + name = "byteorder" 604 + version = "1.5.0" 605 + source = "registry+https://github.com/rust-lang/crates.io-index" 606 + checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 607 + 608 + [[package]] 609 + name = "bytes" 610 + version = "1.6.0" 611 + source = "registry+https://github.com/rust-lang/crates.io-index" 612 + checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" 613 + 614 + [[package]] 615 + name = "bytestring" 616 + version = "1.3.1" 617 + source = "registry+https://github.com/rust-lang/crates.io-index" 618 + checksum = "74d80203ea6b29df88012294f62733de21cfeab47f17b41af3a38bc30a03ee72" 619 + dependencies = [ 620 + "bytes", 621 + ] 622 + 623 + [[package]] 624 + name = "cbc" 625 + version = "0.1.2" 626 + source = "registry+https://github.com/rust-lang/crates.io-index" 627 + checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" 628 + dependencies = [ 629 + "cipher", 630 + ] 631 + 632 + [[package]] 633 + name = "cc" 634 + version = "1.2.6" 635 + source = "registry+https://github.com/rust-lang/crates.io-index" 636 + checksum = "8d6dbb628b8f8555f86d0323c2eb39e3ec81901f4b83e091db8a6a76d316a333" 637 + dependencies = [ 638 + "jobserver", 639 + "libc", 640 + "shlex", 641 + ] 642 + 643 + [[package]] 644 + name = "cfg-if" 645 + version = "1.0.0" 646 + source = "registry+https://github.com/rust-lang/crates.io-index" 647 + checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 648 + 649 + [[package]] 650 + name = "chrono" 651 + version = "0.4.38" 652 + source = "registry+https://github.com/rust-lang/crates.io-index" 653 + checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" 654 + dependencies = [ 655 + "android-tzdata", 656 + "iana-time-zone", 657 + "js-sys", 658 + "num-traits", 659 + "wasm-bindgen", 660 + "windows-targets 0.52.5", 661 + ] 662 + 663 + [[package]] 664 + name = "cipher" 665 + version = "0.4.4" 666 + source = "registry+https://github.com/rust-lang/crates.io-index" 667 + checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" 668 + dependencies = [ 669 + "crypto-common", 670 + "inout", 671 + ] 672 + 673 + [[package]] 674 + name = "colored" 675 + version = "3.0.0" 676 + source = "registry+https://github.com/rust-lang/crates.io-index" 677 + checksum = "fde0e0ec90c9dfb3b4b1a0891a7dcd0e2bffde2f7efed5fe7c9bb00e5bfb915e" 678 + dependencies = [ 679 + "windows-sys 0.52.0", 680 + ] 681 + 682 + [[package]] 683 + name = "concurrent-queue" 684 + version = "2.5.0" 685 + source = "registry+https://github.com/rust-lang/crates.io-index" 686 + checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" 687 + dependencies = [ 688 + "crossbeam-utils", 689 + ] 690 + 691 + [[package]] 692 + name = "console" 693 + version = "0.15.8" 694 + source = "registry+https://github.com/rust-lang/crates.io-index" 695 + checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" 696 + dependencies = [ 697 + "encode_unicode", 698 + "lazy_static", 699 + "libc", 700 + "unicode-width", 701 + "windows-sys 0.52.0", 702 + ] 703 + 704 + [[package]] 705 + name = "convert_case" 706 + version = "0.4.0" 707 + source = "registry+https://github.com/rust-lang/crates.io-index" 708 + checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" 709 + 710 + [[package]] 711 + name = "cookie" 712 + version = "0.16.2" 713 + source = "registry+https://github.com/rust-lang/crates.io-index" 714 + checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" 715 + dependencies = [ 716 + "aes-gcm", 717 + "base64 0.20.0", 718 + "hkdf", 719 + "hmac", 720 + "percent-encoding", 721 + "rand", 722 + "sha2", 723 + "subtle", 724 + "time", 725 + "version_check", 726 + ] 727 + 728 + [[package]] 729 + name = "core-foundation" 730 + version = "0.9.4" 731 + source = "registry+https://github.com/rust-lang/crates.io-index" 732 + checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" 733 + dependencies = [ 734 + "core-foundation-sys", 735 + "libc", 736 + ] 737 + 738 + [[package]] 739 + name = "core-foundation-sys" 740 + version = "0.8.6" 741 + source = "registry+https://github.com/rust-lang/crates.io-index" 742 + checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" 743 + 744 + [[package]] 745 + name = "cpufeatures" 746 + version = "0.2.12" 747 + source = "registry+https://github.com/rust-lang/crates.io-index" 748 + checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" 749 + dependencies = [ 750 + "libc", 751 + ] 752 + 753 + [[package]] 754 + name = "crc-any" 755 + version = "2.5.0" 756 + source = "registry+https://github.com/rust-lang/crates.io-index" 757 + checksum = "a62ec9ff5f7965e4d7280bd5482acd20aadb50d632cf6c1d74493856b011fa73" 758 + dependencies = [ 759 + "debug-helper", 760 + ] 761 + 762 + [[package]] 763 + name = "crc32fast" 764 + version = "1.4.2" 765 + source = "registry+https://github.com/rust-lang/crates.io-index" 766 + checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" 767 + dependencies = [ 768 + "cfg-if", 769 + ] 770 + 771 + [[package]] 772 + name = "crossbeam-utils" 773 + version = "0.8.20" 774 + source = "registry+https://github.com/rust-lang/crates.io-index" 775 + checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" 776 + 777 + [[package]] 778 + name = "crypto-common" 779 + version = "0.1.6" 780 + source = "registry+https://github.com/rust-lang/crates.io-index" 781 + checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" 782 + dependencies = [ 783 + "generic-array", 784 + "rand_core", 785 + "typenum", 786 + ] 787 + 788 + [[package]] 789 + name = "cssparser" 790 + version = "0.31.2" 791 + source = "registry+https://github.com/rust-lang/crates.io-index" 792 + checksum = "5b3df4f93e5fbbe73ec01ec8d3f68bba73107993a5b1e7519273c32db9b0d5be" 793 + dependencies = [ 794 + "cssparser-macros", 795 + "dtoa-short", 796 + "itoa", 797 + "phf 0.11.2", 798 + "smallvec", 799 + ] 800 + 801 + [[package]] 802 + name = "cssparser-macros" 803 + version = "0.6.1" 804 + source = "registry+https://github.com/rust-lang/crates.io-index" 805 + checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" 806 + dependencies = [ 807 + "quote", 808 + "syn 2.0.68", 809 + ] 810 + 811 + [[package]] 812 + name = "ctr" 813 + version = "0.9.2" 814 + source = "registry+https://github.com/rust-lang/crates.io-index" 815 + checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" 816 + dependencies = [ 817 + "cipher", 818 + ] 819 + 820 + [[package]] 821 + name = "debug-helper" 822 + version = "0.3.13" 823 + source = "registry+https://github.com/rust-lang/crates.io-index" 824 + checksum = "f578e8e2c440e7297e008bb5486a3a8a194775224bbc23729b0dbdfaeebf162e" 825 + 826 + [[package]] 827 + name = "deranged" 828 + version = "0.3.11" 829 + source = "registry+https://github.com/rust-lang/crates.io-index" 830 + checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" 831 + dependencies = [ 832 + "powerfmt", 833 + ] 834 + 835 + [[package]] 836 + name = "derive_more" 837 + version = "0.99.18" 838 + source = "registry+https://github.com/rust-lang/crates.io-index" 839 + checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" 840 + dependencies = [ 841 + "convert_case", 842 + "proc-macro2", 843 + "quote", 844 + "rustc_version", 845 + "syn 2.0.68", 846 + ] 847 + 848 + [[package]] 849 + name = "derive_more" 850 + version = "1.0.0" 851 + source = "registry+https://github.com/rust-lang/crates.io-index" 852 + checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" 853 + dependencies = [ 854 + "derive_more-impl", 855 + ] 856 + 857 + [[package]] 858 + name = "derive_more-impl" 859 + version = "1.0.0" 860 + source = "registry+https://github.com/rust-lang/crates.io-index" 861 + checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" 862 + dependencies = [ 863 + "proc-macro2", 864 + "quote", 865 + "syn 2.0.68", 866 + "unicode-xid", 867 + ] 868 + 869 + [[package]] 870 + name = "des" 871 + version = "0.8.1" 872 + source = "registry+https://github.com/rust-lang/crates.io-index" 873 + checksum = "ffdd80ce8ce993de27e9f063a444a4d53ce8e8db4c1f00cc03af5ad5a9867a1e" 874 + dependencies = [ 875 + "cipher", 876 + ] 877 + 878 + [[package]] 879 + name = "digest" 880 + version = "0.10.7" 881 + source = "registry+https://github.com/rust-lang/crates.io-index" 882 + checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" 883 + dependencies = [ 884 + "block-buffer", 885 + "crypto-common", 886 + "subtle", 887 + ] 888 + 889 + [[package]] 890 + name = "dotenv" 891 + version = "0.15.0" 892 + source = "registry+https://github.com/rust-lang/crates.io-index" 893 + checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" 894 + 895 + [[package]] 896 + name = "dtoa" 897 + version = "1.0.9" 898 + source = "registry+https://github.com/rust-lang/crates.io-index" 899 + checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" 900 + 901 + [[package]] 902 + name = "dtoa-short" 903 + version = "0.3.5" 904 + source = "registry+https://github.com/rust-lang/crates.io-index" 905 + checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87" 906 + dependencies = [ 907 + "dtoa", 908 + ] 909 + 910 + [[package]] 911 + name = "ego-tree" 912 + version = "0.6.2" 913 + source = "registry+https://github.com/rust-lang/crates.io-index" 914 + checksum = "3a68a4904193147e0a8dec3314640e6db742afd5f6e634f428a6af230d9b3591" 915 + 916 + [[package]] 917 + name = "encode_unicode" 918 + version = "0.3.6" 919 + source = "registry+https://github.com/rust-lang/crates.io-index" 920 + checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" 921 + 922 + [[package]] 923 + name = "encoding_rs" 924 + version = "0.8.34" 925 + source = "registry+https://github.com/rust-lang/crates.io-index" 926 + checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" 927 + dependencies = [ 928 + "cfg-if", 929 + ] 930 + 931 + [[package]] 932 + name = "equivalent" 933 + version = "1.0.1" 934 + source = "registry+https://github.com/rust-lang/crates.io-index" 935 + checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 936 + 937 + [[package]] 938 + name = "errno" 939 + version = "0.3.9" 940 + source = "registry+https://github.com/rust-lang/crates.io-index" 941 + checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" 942 + dependencies = [ 943 + "libc", 944 + "windows-sys 0.52.0", 945 + ] 946 + 947 + [[package]] 948 + name = "event-listener" 949 + version = "2.5.3" 950 + source = "registry+https://github.com/rust-lang/crates.io-index" 951 + checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" 952 + 953 + [[package]] 954 + name = "event-listener" 955 + version = "5.3.1" 956 + source = "registry+https://github.com/rust-lang/crates.io-index" 957 + checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" 958 + dependencies = [ 959 + "concurrent-queue", 960 + "parking", 961 + "pin-project-lite", 962 + ] 963 + 964 + [[package]] 965 + name = "event-listener-strategy" 966 + version = "0.5.2" 967 + source = "registry+https://github.com/rust-lang/crates.io-index" 968 + checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" 969 + dependencies = [ 970 + "event-listener 5.3.1", 971 + "pin-project-lite", 972 + ] 973 + 974 + [[package]] 975 + name = "fallible-iterator" 976 + version = "0.2.0" 977 + source = "registry+https://github.com/rust-lang/crates.io-index" 978 + checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" 979 + 980 + [[package]] 981 + name = "fallible-iterator" 982 + version = "0.3.0" 983 + source = "registry+https://github.com/rust-lang/crates.io-index" 984 + checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" 985 + 986 + [[package]] 987 + name = "fallible-streaming-iterator" 988 + version = "0.1.9" 989 + source = "registry+https://github.com/rust-lang/crates.io-index" 990 + checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" 991 + 992 + [[package]] 993 + name = "fastrand" 994 + version = "1.9.0" 995 + source = "registry+https://github.com/rust-lang/crates.io-index" 996 + checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" 997 + dependencies = [ 998 + "instant", 999 + ] 1000 + 1001 + [[package]] 1002 + name = "fastrand" 1003 + version = "2.1.0" 1004 + source = "registry+https://github.com/rust-lang/crates.io-index" 1005 + checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" 1006 + 1007 + [[package]] 1008 + name = "flate2" 1009 + version = "1.0.30" 1010 + source = "registry+https://github.com/rust-lang/crates.io-index" 1011 + checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" 1012 + dependencies = [ 1013 + "crc32fast", 1014 + "miniz_oxide", 1015 + ] 1016 + 1017 + [[package]] 1018 + name = "fnv" 1019 + version = "1.0.7" 1020 + source = "registry+https://github.com/rust-lang/crates.io-index" 1021 + checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 1022 + 1023 + [[package]] 1024 + name = "foreign-types" 1025 + version = "0.3.2" 1026 + source = "registry+https://github.com/rust-lang/crates.io-index" 1027 + checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 1028 + dependencies = [ 1029 + "foreign-types-shared", 1030 + ] 1031 + 1032 + [[package]] 1033 + name = "foreign-types-shared" 1034 + version = "0.1.1" 1035 + source = "registry+https://github.com/rust-lang/crates.io-index" 1036 + checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 1037 + 1038 + [[package]] 1039 + name = "form_urlencoded" 1040 + version = "1.2.1" 1041 + source = "registry+https://github.com/rust-lang/crates.io-index" 1042 + checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" 1043 + dependencies = [ 1044 + "percent-encoding", 1045 + ] 1046 + 1047 + [[package]] 1048 + name = "futf" 1049 + version = "0.1.5" 1050 + source = "registry+https://github.com/rust-lang/crates.io-index" 1051 + checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" 1052 + dependencies = [ 1053 + "mac", 1054 + "new_debug_unreachable", 1055 + ] 1056 + 1057 + [[package]] 1058 + name = "futures" 1059 + version = "0.3.30" 1060 + source = "registry+https://github.com/rust-lang/crates.io-index" 1061 + checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" 1062 + dependencies = [ 1063 + "futures-channel", 1064 + "futures-core", 1065 + "futures-executor", 1066 + "futures-io", 1067 + "futures-sink", 1068 + "futures-task", 1069 + "futures-util", 1070 + ] 1071 + 1072 + [[package]] 1073 + name = "futures-channel" 1074 + version = "0.3.30" 1075 + source = "registry+https://github.com/rust-lang/crates.io-index" 1076 + checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" 1077 + dependencies = [ 1078 + "futures-core", 1079 + "futures-sink", 1080 + ] 1081 + 1082 + [[package]] 1083 + name = "futures-core" 1084 + version = "0.3.30" 1085 + source = "registry+https://github.com/rust-lang/crates.io-index" 1086 + checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" 1087 + 1088 + [[package]] 1089 + name = "futures-executor" 1090 + version = "0.3.30" 1091 + source = "registry+https://github.com/rust-lang/crates.io-index" 1092 + checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" 1093 + dependencies = [ 1094 + "futures-core", 1095 + "futures-task", 1096 + "futures-util", 1097 + ] 1098 + 1099 + [[package]] 1100 + name = "futures-io" 1101 + version = "0.3.30" 1102 + source = "registry+https://github.com/rust-lang/crates.io-index" 1103 + checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" 1104 + 1105 + [[package]] 1106 + name = "futures-lite" 1107 + version = "1.13.0" 1108 + source = "registry+https://github.com/rust-lang/crates.io-index" 1109 + checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" 1110 + dependencies = [ 1111 + "fastrand 1.9.0", 1112 + "futures-core", 1113 + "futures-io", 1114 + "memchr", 1115 + "parking", 1116 + "pin-project-lite", 1117 + "waker-fn", 1118 + ] 1119 + 1120 + [[package]] 1121 + name = "futures-lite" 1122 + version = "2.3.0" 1123 + source = "registry+https://github.com/rust-lang/crates.io-index" 1124 + checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" 1125 + dependencies = [ 1126 + "fastrand 2.1.0", 1127 + "futures-core", 1128 + "futures-io", 1129 + "parking", 1130 + "pin-project-lite", 1131 + ] 1132 + 1133 + [[package]] 1134 + name = "futures-macro" 1135 + version = "0.3.30" 1136 + source = "registry+https://github.com/rust-lang/crates.io-index" 1137 + checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" 1138 + dependencies = [ 1139 + "proc-macro2", 1140 + "quote", 1141 + "syn 2.0.68", 1142 + ] 1143 + 1144 + [[package]] 1145 + name = "futures-sink" 1146 + version = "0.3.30" 1147 + source = "registry+https://github.com/rust-lang/crates.io-index" 1148 + checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" 1149 + 1150 + [[package]] 1151 + name = "futures-task" 1152 + version = "0.3.30" 1153 + source = "registry+https://github.com/rust-lang/crates.io-index" 1154 + checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" 1155 + 1156 + [[package]] 1157 + name = "futures-util" 1158 + version = "0.3.30" 1159 + source = "registry+https://github.com/rust-lang/crates.io-index" 1160 + checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" 1161 + dependencies = [ 1162 + "futures-channel", 1163 + "futures-core", 1164 + "futures-io", 1165 + "futures-macro", 1166 + "futures-sink", 1167 + "futures-task", 1168 + "memchr", 1169 + "pin-project-lite", 1170 + "pin-utils", 1171 + "slab", 1172 + ] 1173 + 1174 + [[package]] 1175 + name = "fxhash" 1176 + version = "0.2.1" 1177 + source = "registry+https://github.com/rust-lang/crates.io-index" 1178 + checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" 1179 + dependencies = [ 1180 + "byteorder", 1181 + ] 1182 + 1183 + [[package]] 1184 + name = "generic-array" 1185 + version = "0.14.7" 1186 + source = "registry+https://github.com/rust-lang/crates.io-index" 1187 + checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" 1188 + dependencies = [ 1189 + "typenum", 1190 + "version_check", 1191 + ] 1192 + 1193 + [[package]] 1194 + name = "getopts" 1195 + version = "0.2.21" 1196 + source = "registry+https://github.com/rust-lang/crates.io-index" 1197 + checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" 1198 + dependencies = [ 1199 + "unicode-width", 1200 + ] 1201 + 1202 + [[package]] 1203 + name = "getrandom" 1204 + version = "0.2.15" 1205 + source = "registry+https://github.com/rust-lang/crates.io-index" 1206 + checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" 1207 + dependencies = [ 1208 + "cfg-if", 1209 + "libc", 1210 + "wasi", 1211 + ] 1212 + 1213 + [[package]] 1214 + name = "ghash" 1215 + version = "0.5.1" 1216 + source = "registry+https://github.com/rust-lang/crates.io-index" 1217 + checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" 1218 + dependencies = [ 1219 + "opaque-debug", 1220 + "polyval", 1221 + ] 1222 + 1223 + [[package]] 1224 + name = "gimli" 1225 + version = "0.29.0" 1226 + source = "registry+https://github.com/rust-lang/crates.io-index" 1227 + checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" 1228 + 1229 + [[package]] 1230 + name = "gloo-timers" 1231 + version = "0.2.6" 1232 + source = "registry+https://github.com/rust-lang/crates.io-index" 1233 + checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" 1234 + dependencies = [ 1235 + "futures-channel", 1236 + "futures-core", 1237 + "js-sys", 1238 + "wasm-bindgen", 1239 + ] 1240 + 1241 + [[package]] 1242 + name = "h2" 1243 + version = "0.3.26" 1244 + source = "registry+https://github.com/rust-lang/crates.io-index" 1245 + checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" 1246 + dependencies = [ 1247 + "bytes", 1248 + "fnv", 1249 + "futures-core", 1250 + "futures-sink", 1251 + "futures-util", 1252 + "http 0.2.12", 1253 + "indexmap", 1254 + "slab", 1255 + "tokio", 1256 + "tokio-util", 1257 + "tracing", 1258 + ] 1259 + 1260 + [[package]] 1261 + name = "h2" 1262 + version = "0.4.5" 1263 + source = "registry+https://github.com/rust-lang/crates.io-index" 1264 + checksum = "fa82e28a107a8cc405f0839610bdc9b15f1e25ec7d696aa5cf173edbcb1486ab" 1265 + dependencies = [ 1266 + "atomic-waker", 1267 + "bytes", 1268 + "fnv", 1269 + "futures-core", 1270 + "futures-sink", 1271 + "http 1.1.0", 1272 + "indexmap", 1273 + "slab", 1274 + "tokio", 1275 + "tokio-util", 1276 + "tracing", 1277 + ] 1278 + 1279 + [[package]] 1280 + name = "handlebars" 1281 + version = "6.2.0" 1282 + source = "registry+https://github.com/rust-lang/crates.io-index" 1283 + checksum = "fd4ccde012831f9a071a637b0d4e31df31c0f6c525784b35ae76a9ac6bc1e315" 1284 + dependencies = [ 1285 + "log", 1286 + "num-order", 1287 + "pest", 1288 + "pest_derive", 1289 + "serde", 1290 + "serde_json", 1291 + "thiserror", 1292 + ] 1293 + 1294 + [[package]] 1295 + name = "hashbrown" 1296 + version = "0.14.5" 1297 + source = "registry+https://github.com/rust-lang/crates.io-index" 1298 + checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" 1299 + dependencies = [ 1300 + "ahash", 1301 + ] 1302 + 1303 + [[package]] 1304 + name = "hashlink" 1305 + version = "0.9.1" 1306 + source = "registry+https://github.com/rust-lang/crates.io-index" 1307 + checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" 1308 + dependencies = [ 1309 + "hashbrown", 1310 + ] 1311 + 1312 + [[package]] 1313 + name = "hermit-abi" 1314 + version = "0.3.9" 1315 + source = "registry+https://github.com/rust-lang/crates.io-index" 1316 + checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" 1317 + 1318 + [[package]] 1319 + name = "hermit-abi" 1320 + version = "0.4.0" 1321 + source = "registry+https://github.com/rust-lang/crates.io-index" 1322 + checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" 1323 + 1324 + [[package]] 1325 + name = "hkdf" 1326 + version = "0.12.4" 1327 + source = "registry+https://github.com/rust-lang/crates.io-index" 1328 + checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" 1329 + dependencies = [ 1330 + "hmac", 1331 + ] 1332 + 1333 + [[package]] 1334 + name = "hmac" 1335 + version = "0.12.1" 1336 + source = "registry+https://github.com/rust-lang/crates.io-index" 1337 + checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" 1338 + dependencies = [ 1339 + "digest", 1340 + ] 1341 + 1342 + [[package]] 1343 + name = "home" 1344 + version = "0.5.9" 1345 + source = "registry+https://github.com/rust-lang/crates.io-index" 1346 + checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" 1347 + dependencies = [ 1348 + "windows-sys 0.52.0", 1349 + ] 1350 + 1351 + [[package]] 1352 + name = "html5ever" 1353 + version = "0.26.0" 1354 + source = "registry+https://github.com/rust-lang/crates.io-index" 1355 + checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" 1356 + dependencies = [ 1357 + "log", 1358 + "mac", 1359 + "markup5ever", 1360 + "proc-macro2", 1361 + "quote", 1362 + "syn 1.0.109", 1363 + ] 1364 + 1365 + [[package]] 1366 + name = "http" 1367 + version = "0.2.12" 1368 + source = "registry+https://github.com/rust-lang/crates.io-index" 1369 + checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" 1370 + dependencies = [ 1371 + "bytes", 1372 + "fnv", 1373 + "itoa", 1374 + ] 1375 + 1376 + [[package]] 1377 + name = "http" 1378 + version = "1.1.0" 1379 + source = "registry+https://github.com/rust-lang/crates.io-index" 1380 + checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" 1381 + dependencies = [ 1382 + "bytes", 1383 + "fnv", 1384 + "itoa", 1385 + ] 1386 + 1387 + [[package]] 1388 + name = "http-body" 1389 + version = "1.0.0" 1390 + source = "registry+https://github.com/rust-lang/crates.io-index" 1391 + checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" 1392 + dependencies = [ 1393 + "bytes", 1394 + "http 1.1.0", 1395 + ] 1396 + 1397 + [[package]] 1398 + name = "http-body-util" 1399 + version = "0.1.2" 1400 + source = "registry+https://github.com/rust-lang/crates.io-index" 1401 + checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" 1402 + dependencies = [ 1403 + "bytes", 1404 + "futures-util", 1405 + "http 1.1.0", 1406 + "http-body", 1407 + "pin-project-lite", 1408 + ] 1409 + 1410 + [[package]] 1411 + name = "httparse" 1412 + version = "1.9.4" 1413 + source = "registry+https://github.com/rust-lang/crates.io-index" 1414 + checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" 1415 + 1416 + [[package]] 1417 + name = "httpdate" 1418 + version = "1.0.3" 1419 + source = "registry+https://github.com/rust-lang/crates.io-index" 1420 + checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" 1421 + 1422 + [[package]] 1423 + name = "hyper" 1424 + version = "1.3.1" 1425 + source = "registry+https://github.com/rust-lang/crates.io-index" 1426 + checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d" 1427 + dependencies = [ 1428 + "bytes", 1429 + "futures-channel", 1430 + "futures-util", 1431 + "h2 0.4.5", 1432 + "http 1.1.0", 1433 + "http-body", 1434 + "httparse", 1435 + "itoa", 1436 + "pin-project-lite", 1437 + "smallvec", 1438 + "tokio", 1439 + "want", 1440 + ] 1441 + 1442 + [[package]] 1443 + name = "hyper-rustls" 1444 + version = "0.27.2" 1445 + source = "registry+https://github.com/rust-lang/crates.io-index" 1446 + checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155" 1447 + dependencies = [ 1448 + "futures-util", 1449 + "http 1.1.0", 1450 + "hyper", 1451 + "hyper-util", 1452 + "rustls", 1453 + "rustls-pki-types", 1454 + "tokio", 1455 + "tokio-rustls", 1456 + "tower-service", 1457 + ] 1458 + 1459 + [[package]] 1460 + name = "hyper-tls" 1461 + version = "0.6.0" 1462 + source = "registry+https://github.com/rust-lang/crates.io-index" 1463 + checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" 1464 + dependencies = [ 1465 + "bytes", 1466 + "http-body-util", 1467 + "hyper", 1468 + "hyper-util", 1469 + "native-tls", 1470 + "tokio", 1471 + "tokio-native-tls", 1472 + "tower-service", 1473 + ] 1474 + 1475 + [[package]] 1476 + name = "hyper-util" 1477 + version = "0.1.5" 1478 + source = "registry+https://github.com/rust-lang/crates.io-index" 1479 + checksum = "7b875924a60b96e5d7b9ae7b066540b1dd1cbd90d1828f54c92e02a283351c56" 1480 + dependencies = [ 1481 + "bytes", 1482 + "futures-channel", 1483 + "futures-util", 1484 + "http 1.1.0", 1485 + "http-body", 1486 + "hyper", 1487 + "pin-project-lite", 1488 + "socket2 0.5.7", 1489 + "tokio", 1490 + "tower", 1491 + "tower-service", 1492 + "tracing", 1493 + ] 1494 + 1495 + [[package]] 1496 + name = "iana-time-zone" 1497 + version = "0.1.60" 1498 + source = "registry+https://github.com/rust-lang/crates.io-index" 1499 + checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" 1500 + dependencies = [ 1501 + "android_system_properties", 1502 + "core-foundation-sys", 1503 + "iana-time-zone-haiku", 1504 + "js-sys", 1505 + "wasm-bindgen", 1506 + "windows-core", 1507 + ] 1508 + 1509 + [[package]] 1510 + name = "iana-time-zone-haiku" 1511 + version = "0.1.2" 1512 + source = "registry+https://github.com/rust-lang/crates.io-index" 1513 + checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 1514 + dependencies = [ 1515 + "cc", 1516 + ] 1517 + 1518 + [[package]] 1519 + name = "idna" 1520 + version = "0.5.0" 1521 + source = "registry+https://github.com/rust-lang/crates.io-index" 1522 + checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" 1523 + dependencies = [ 1524 + "unicode-bidi", 1525 + "unicode-normalization", 1526 + ] 1527 + 1528 + [[package]] 1529 + name = "indexmap" 1530 + version = "2.2.6" 1531 + source = "registry+https://github.com/rust-lang/crates.io-index" 1532 + checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" 1533 + dependencies = [ 1534 + "equivalent", 1535 + "hashbrown", 1536 + ] 1537 + 1538 + [[package]] 1539 + name = "indicatif" 1540 + version = "0.17.8" 1541 + source = "registry+https://github.com/rust-lang/crates.io-index" 1542 + checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3" 1543 + dependencies = [ 1544 + "console", 1545 + "instant", 1546 + "number_prefix", 1547 + "portable-atomic", 1548 + "unicode-width", 1549 + ] 1550 + 1551 + [[package]] 1552 + name = "inout" 1553 + version = "0.1.3" 1554 + source = "registry+https://github.com/rust-lang/crates.io-index" 1555 + checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" 1556 + dependencies = [ 1557 + "block-padding", 1558 + "generic-array", 1559 + ] 1560 + 1561 + [[package]] 1562 + name = "instant" 1563 + version = "0.1.13" 1564 + source = "registry+https://github.com/rust-lang/crates.io-index" 1565 + checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" 1566 + dependencies = [ 1567 + "cfg-if", 1568 + ] 1569 + 1570 + [[package]] 1571 + name = "io-lifetimes" 1572 + version = "1.0.11" 1573 + source = "registry+https://github.com/rust-lang/crates.io-index" 1574 + checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" 1575 + dependencies = [ 1576 + "hermit-abi 0.3.9", 1577 + "libc", 1578 + "windows-sys 0.48.0", 1579 + ] 1580 + 1581 + [[package]] 1582 + name = "ipnet" 1583 + version = "2.9.0" 1584 + source = "registry+https://github.com/rust-lang/crates.io-index" 1585 + checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" 1586 + 1587 + [[package]] 1588 + name = "itoa" 1589 + version = "1.0.11" 1590 + source = "registry+https://github.com/rust-lang/crates.io-index" 1591 + checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" 1592 + 1593 + [[package]] 1594 + name = "jobserver" 1595 + version = "0.1.31" 1596 + source = "registry+https://github.com/rust-lang/crates.io-index" 1597 + checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" 1598 + dependencies = [ 1599 + "libc", 1600 + ] 1601 + 1602 + [[package]] 1603 + name = "js-sys" 1604 + version = "0.3.69" 1605 + source = "registry+https://github.com/rust-lang/crates.io-index" 1606 + checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" 1607 + dependencies = [ 1608 + "wasm-bindgen", 1609 + ] 1610 + 1611 + [[package]] 1612 + name = "kv-log-macro" 1613 + version = "1.0.7" 1614 + source = "registry+https://github.com/rust-lang/crates.io-index" 1615 + checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" 1616 + dependencies = [ 1617 + "log", 1618 + ] 1619 + 1620 + [[package]] 1621 + name = "language-tags" 1622 + version = "0.3.2" 1623 + source = "registry+https://github.com/rust-lang/crates.io-index" 1624 + checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" 1625 + 1626 + [[package]] 1627 + name = "lazy_static" 1628 + version = "1.5.0" 1629 + source = "registry+https://github.com/rust-lang/crates.io-index" 1630 + checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 1631 + 1632 + [[package]] 1633 + name = "libc" 1634 + version = "0.2.155" 1635 + source = "registry+https://github.com/rust-lang/crates.io-index" 1636 + checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" 1637 + 1638 + [[package]] 1639 + name = "libsqlite3-sys" 1640 + version = "0.30.1" 1641 + source = "registry+https://github.com/rust-lang/crates.io-index" 1642 + checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" 1643 + dependencies = [ 1644 + "cc", 1645 + "pkg-config", 1646 + "vcpkg", 1647 + ] 1648 + 1649 + [[package]] 1650 + name = "linux-raw-sys" 1651 + version = "0.3.8" 1652 + source = "registry+https://github.com/rust-lang/crates.io-index" 1653 + checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" 1654 + 1655 + [[package]] 1656 + name = "linux-raw-sys" 1657 + version = "0.4.14" 1658 + source = "registry+https://github.com/rust-lang/crates.io-index" 1659 + checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" 1660 + 1661 + [[package]] 1662 + name = "local-channel" 1663 + version = "0.1.5" 1664 + source = "registry+https://github.com/rust-lang/crates.io-index" 1665 + checksum = "b6cbc85e69b8df4b8bb8b89ec634e7189099cea8927a276b7384ce5488e53ec8" 1666 + dependencies = [ 1667 + "futures-core", 1668 + "futures-sink", 1669 + "local-waker", 1670 + ] 1671 + 1672 + [[package]] 1673 + name = "local-waker" 1674 + version = "0.1.4" 1675 + source = "registry+https://github.com/rust-lang/crates.io-index" 1676 + checksum = "4d873d7c67ce09b42110d801813efbc9364414e356be9935700d368351657487" 1677 + 1678 + [[package]] 1679 + name = "lock_api" 1680 + version = "0.4.12" 1681 + source = "registry+https://github.com/rust-lang/crates.io-index" 1682 + checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" 1683 + dependencies = [ 1684 + "autocfg", 1685 + "scopeguard", 1686 + ] 1687 + 1688 + [[package]] 1689 + name = "log" 1690 + version = "0.4.22" 1691 + source = "registry+https://github.com/rust-lang/crates.io-index" 1692 + checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" 1693 + dependencies = [ 1694 + "value-bag", 1695 + ] 1696 + 1697 + [[package]] 1698 + name = "lumina-urls" 1699 + version = "0.1.0" 1700 + dependencies = [ 1701 + "regex", 1702 + "reqwest", 1703 + "scraper", 1704 + "serde", 1705 + "serde_json", 1706 + ] 1707 + 1708 + [[package]] 1709 + name = "mac" 1710 + version = "0.1.1" 1711 + source = "registry+https://github.com/rust-lang/crates.io-index" 1712 + checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" 1713 + 1714 + [[package]] 1715 + name = "magic-crypt" 1716 + version = "4.0.1" 1717 + source = "registry+https://github.com/rust-lang/crates.io-index" 1718 + checksum = "844b6169eeaae32ae8a61855964331a67f12d2afba9170303fbd3e3c2a861a52" 1719 + dependencies = [ 1720 + "aes", 1721 + "base64 0.22.1", 1722 + "cbc", 1723 + "crc-any", 1724 + "des", 1725 + "md-5", 1726 + "sha2", 1727 + "tiger", 1728 + ] 1729 + 1730 + [[package]] 1731 + name = "markdown" 1732 + version = "1.0.0-alpha.18" 1733 + source = "registry+https://github.com/rust-lang/crates.io-index" 1734 + checksum = "4e61c5c85b392273c4d4ea546e6399ace3e3db172ab01b6de8f3d398d1dbd2ec" 1735 + dependencies = [ 1736 + "unicode-id", 1737 + ] 1738 + 1739 + [[package]] 1740 + name = "markup5ever" 1741 + version = "0.11.0" 1742 + source = "registry+https://github.com/rust-lang/crates.io-index" 1743 + checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" 1744 + dependencies = [ 1745 + "log", 1746 + "phf 0.10.1", 1747 + "phf_codegen", 1748 + "string_cache", 1749 + "string_cache_codegen", 1750 + "tendril", 1751 + ] 1752 + 1753 + [[package]] 1754 + name = "md-5" 1755 + version = "0.10.6" 1756 + source = "registry+https://github.com/rust-lang/crates.io-index" 1757 + checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" 1758 + dependencies = [ 1759 + "cfg-if", 1760 + "digest", 1761 + ] 1762 + 1763 + [[package]] 1764 + name = "memchr" 1765 + version = "2.7.4" 1766 + source = "registry+https://github.com/rust-lang/crates.io-index" 1767 + checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 1768 + 1769 + [[package]] 1770 + name = "mime" 1771 + version = "0.3.17" 1772 + source = "registry+https://github.com/rust-lang/crates.io-index" 1773 + checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" 1774 + 1775 + [[package]] 1776 + name = "miniz_oxide" 1777 + version = "0.7.4" 1778 + source = "registry+https://github.com/rust-lang/crates.io-index" 1779 + checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" 1780 + dependencies = [ 1781 + "adler", 1782 + ] 1783 + 1784 + [[package]] 1785 + name = "mio" 1786 + version = "0.8.11" 1787 + source = "registry+https://github.com/rust-lang/crates.io-index" 1788 + checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" 1789 + dependencies = [ 1790 + "libc", 1791 + "log", 1792 + "wasi", 1793 + "windows-sys 0.48.0", 1794 + ] 1795 + 1796 + [[package]] 1797 + name = "native-tls" 1798 + version = "0.2.12" 1799 + source = "registry+https://github.com/rust-lang/crates.io-index" 1800 + checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" 1801 + dependencies = [ 1802 + "libc", 1803 + "log", 1804 + "openssl", 1805 + "openssl-probe", 1806 + "openssl-sys", 1807 + "schannel", 1808 + "security-framework", 1809 + "security-framework-sys", 1810 + "tempfile", 1811 + ] 1812 + 1813 + [[package]] 1814 + name = "new_debug_unreachable" 1815 + version = "1.0.6" 1816 + source = "registry+https://github.com/rust-lang/crates.io-index" 1817 + checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" 1818 + 1819 + [[package]] 1820 + name = "num-conv" 1821 + version = "0.1.0" 1822 + source = "registry+https://github.com/rust-lang/crates.io-index" 1823 + checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" 1824 + 1825 + [[package]] 1826 + name = "num-modular" 1827 + version = "0.6.1" 1828 + source = "registry+https://github.com/rust-lang/crates.io-index" 1829 + checksum = "17bb261bf36fa7d83f4c294f834e91256769097b3cb505d44831e0a179ac647f" 1830 + 1831 + [[package]] 1832 + name = "num-order" 1833 + version = "1.2.0" 1834 + source = "registry+https://github.com/rust-lang/crates.io-index" 1835 + checksum = "537b596b97c40fcf8056d153049eb22f481c17ebce72a513ec9286e4986d1bb6" 1836 + dependencies = [ 1837 + "num-modular", 1838 + ] 1839 + 1840 + [[package]] 1841 + name = "num-traits" 1842 + version = "0.2.19" 1843 + source = "registry+https://github.com/rust-lang/crates.io-index" 1844 + checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 1845 + dependencies = [ 1846 + "autocfg", 1847 + ] 1848 + 1849 + [[package]] 1850 + name = "num_cpus" 1851 + version = "1.16.0" 1852 + source = "registry+https://github.com/rust-lang/crates.io-index" 1853 + checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" 1854 + dependencies = [ 1855 + "hermit-abi 0.3.9", 1856 + "libc", 1857 + ] 1858 + 1859 + [[package]] 1860 + name = "num_threads" 1861 + version = "0.1.7" 1862 + source = "registry+https://github.com/rust-lang/crates.io-index" 1863 + checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" 1864 + dependencies = [ 1865 + "libc", 1866 + ] 1867 + 1868 + [[package]] 1869 + name = "number_prefix" 1870 + version = "0.4.0" 1871 + source = "registry+https://github.com/rust-lang/crates.io-index" 1872 + checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" 1873 + 1874 + [[package]] 1875 + name = "object" 1876 + version = "0.36.0" 1877 + source = "registry+https://github.com/rust-lang/crates.io-index" 1878 + checksum = "576dfe1fc8f9df304abb159d767a29d0476f7750fbf8aa7ad07816004a207434" 1879 + dependencies = [ 1880 + "memchr", 1881 + ] 1882 + 1883 + [[package]] 1884 + name = "once_cell" 1885 + version = "1.19.0" 1886 + source = "registry+https://github.com/rust-lang/crates.io-index" 1887 + checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" 1888 + 1889 + [[package]] 1890 + name = "opaque-debug" 1891 + version = "0.3.1" 1892 + source = "registry+https://github.com/rust-lang/crates.io-index" 1893 + checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" 1894 + 1895 + [[package]] 1896 + name = "openssl" 1897 + version = "0.10.66" 1898 + source = "registry+https://github.com/rust-lang/crates.io-index" 1899 + checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" 1900 + dependencies = [ 1901 + "bitflags 2.6.0", 1902 + "cfg-if", 1903 + "foreign-types", 1904 + "libc", 1905 + "once_cell", 1906 + "openssl-macros", 1907 + "openssl-sys", 1908 + ] 1909 + 1910 + [[package]] 1911 + name = "openssl-macros" 1912 + version = "0.1.1" 1913 + source = "registry+https://github.com/rust-lang/crates.io-index" 1914 + checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" 1915 + dependencies = [ 1916 + "proc-macro2", 1917 + "quote", 1918 + "syn 2.0.68", 1919 + ] 1920 + 1921 + [[package]] 1922 + name = "openssl-probe" 1923 + version = "0.1.5" 1924 + source = "registry+https://github.com/rust-lang/crates.io-index" 1925 + checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" 1926 + 1927 + [[package]] 1928 + name = "openssl-sys" 1929 + version = "0.9.103" 1930 + source = "registry+https://github.com/rust-lang/crates.io-index" 1931 + checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" 1932 + dependencies = [ 1933 + "cc", 1934 + "libc", 1935 + "pkg-config", 1936 + "vcpkg", 1937 + ] 1938 + 1939 + [[package]] 1940 + name = "parking" 1941 + version = "2.2.0" 1942 + source = "registry+https://github.com/rust-lang/crates.io-index" 1943 + checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" 1944 + 1945 + [[package]] 1946 + name = "parking_lot" 1947 + version = "0.12.3" 1948 + source = "registry+https://github.com/rust-lang/crates.io-index" 1949 + checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" 1950 + dependencies = [ 1951 + "lock_api", 1952 + "parking_lot_core", 1953 + ] 1954 + 1955 + [[package]] 1956 + name = "parking_lot_core" 1957 + version = "0.9.10" 1958 + source = "registry+https://github.com/rust-lang/crates.io-index" 1959 + checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" 1960 + dependencies = [ 1961 + "cfg-if", 1962 + "libc", 1963 + "redox_syscall", 1964 + "smallvec", 1965 + "windows-targets 0.52.5", 1966 + ] 1967 + 1968 + [[package]] 1969 + name = "password-hash" 1970 + version = "0.5.0" 1971 + source = "registry+https://github.com/rust-lang/crates.io-index" 1972 + checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" 1973 + dependencies = [ 1974 + "base64ct", 1975 + "rand_core", 1976 + "subtle", 1977 + ] 1978 + 1979 + [[package]] 1980 + name = "paste" 1981 + version = "1.0.15" 1982 + source = "registry+https://github.com/rust-lang/crates.io-index" 1983 + checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" 1984 + 1985 + [[package]] 1986 + name = "pbkdf2" 1987 + version = "0.12.2" 1988 + source = "registry+https://github.com/rust-lang/crates.io-index" 1989 + checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" 1990 + dependencies = [ 1991 + "digest", 1992 + "hmac", 1993 + ] 1994 + 1995 + [[package]] 1996 + name = "percent-encoding" 1997 + version = "2.3.1" 1998 + source = "registry+https://github.com/rust-lang/crates.io-index" 1999 + checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 2000 + 2001 + [[package]] 2002 + name = "pest" 2003 + version = "2.7.10" 2004 + source = "registry+https://github.com/rust-lang/crates.io-index" 2005 + checksum = "560131c633294438da9f7c4b08189194b20946c8274c6b9e38881a7874dc8ee8" 2006 + dependencies = [ 2007 + "memchr", 2008 + "thiserror", 2009 + "ucd-trie", 2010 + ] 2011 + 2012 + [[package]] 2013 + name = "pest_derive" 2014 + version = "2.7.10" 2015 + source = "registry+https://github.com/rust-lang/crates.io-index" 2016 + checksum = "26293c9193fbca7b1a3bf9b79dc1e388e927e6cacaa78b4a3ab705a1d3d41459" 2017 + dependencies = [ 2018 + "pest", 2019 + "pest_generator", 2020 + ] 2021 + 2022 + [[package]] 2023 + name = "pest_generator" 2024 + version = "2.7.10" 2025 + source = "registry+https://github.com/rust-lang/crates.io-index" 2026 + checksum = "3ec22af7d3fb470a85dd2ca96b7c577a1eb4ef6f1683a9fe9a8c16e136c04687" 2027 + dependencies = [ 2028 + "pest", 2029 + "pest_meta", 2030 + "proc-macro2", 2031 + "quote", 2032 + "syn 2.0.68", 2033 + ] 2034 + 2035 + [[package]] 2036 + name = "pest_meta" 2037 + version = "2.7.10" 2038 + source = "registry+https://github.com/rust-lang/crates.io-index" 2039 + checksum = "d7a240022f37c361ec1878d646fc5b7d7c4d28d5946e1a80ad5a7a4f4ca0bdcd" 2040 + dependencies = [ 2041 + "once_cell", 2042 + "pest", 2043 + "sha2", 2044 + ] 2045 + 2046 + [[package]] 2047 + name = "phf" 2048 + version = "0.10.1" 2049 + source = "registry+https://github.com/rust-lang/crates.io-index" 2050 + checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" 2051 + dependencies = [ 2052 + "phf_shared 0.10.0", 2053 + ] 2054 + 2055 + [[package]] 2056 + name = "phf" 2057 + version = "0.11.2" 2058 + source = "registry+https://github.com/rust-lang/crates.io-index" 2059 + checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" 2060 + dependencies = [ 2061 + "phf_macros", 2062 + "phf_shared 0.11.2", 2063 + ] 2064 + 2065 + [[package]] 2066 + name = "phf_codegen" 2067 + version = "0.10.0" 2068 + source = "registry+https://github.com/rust-lang/crates.io-index" 2069 + checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" 2070 + dependencies = [ 2071 + "phf_generator 0.10.0", 2072 + "phf_shared 0.10.0", 2073 + ] 2074 + 2075 + [[package]] 2076 + name = "phf_generator" 2077 + version = "0.10.0" 2078 + source = "registry+https://github.com/rust-lang/crates.io-index" 2079 + checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" 2080 + dependencies = [ 2081 + "phf_shared 0.10.0", 2082 + "rand", 2083 + ] 2084 + 2085 + [[package]] 2086 + name = "phf_generator" 2087 + version = "0.11.2" 2088 + source = "registry+https://github.com/rust-lang/crates.io-index" 2089 + checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" 2090 + dependencies = [ 2091 + "phf_shared 0.11.2", 2092 + "rand", 2093 + ] 2094 + 2095 + [[package]] 2096 + name = "phf_macros" 2097 + version = "0.11.2" 2098 + source = "registry+https://github.com/rust-lang/crates.io-index" 2099 + checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" 2100 + dependencies = [ 2101 + "phf_generator 0.11.2", 2102 + "phf_shared 0.11.2", 2103 + "proc-macro2", 2104 + "quote", 2105 + "syn 2.0.68", 2106 + ] 2107 + 2108 + [[package]] 2109 + name = "phf_shared" 2110 + version = "0.10.0" 2111 + source = "registry+https://github.com/rust-lang/crates.io-index" 2112 + checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" 2113 + dependencies = [ 2114 + "siphasher", 2115 + ] 2116 + 2117 + [[package]] 2118 + name = "phf_shared" 2119 + version = "0.11.2" 2120 + source = "registry+https://github.com/rust-lang/crates.io-index" 2121 + checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" 2122 + dependencies = [ 2123 + "siphasher", 2124 + ] 2125 + 2126 + [[package]] 2127 + name = "pin-project" 2128 + version = "1.1.5" 2129 + source = "registry+https://github.com/rust-lang/crates.io-index" 2130 + checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" 2131 + dependencies = [ 2132 + "pin-project-internal", 2133 + ] 2134 + 2135 + [[package]] 2136 + name = "pin-project-internal" 2137 + version = "1.1.5" 2138 + source = "registry+https://github.com/rust-lang/crates.io-index" 2139 + checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" 2140 + dependencies = [ 2141 + "proc-macro2", 2142 + "quote", 2143 + "syn 2.0.68", 2144 + ] 2145 + 2146 + [[package]] 2147 + name = "pin-project-lite" 2148 + version = "0.2.14" 2149 + source = "registry+https://github.com/rust-lang/crates.io-index" 2150 + checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" 2151 + 2152 + [[package]] 2153 + name = "pin-utils" 2154 + version = "0.1.0" 2155 + source = "registry+https://github.com/rust-lang/crates.io-index" 2156 + checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 2157 + 2158 + [[package]] 2159 + name = "piper" 2160 + version = "0.2.3" 2161 + source = "registry+https://github.com/rust-lang/crates.io-index" 2162 + checksum = "ae1d5c74c9876f070d3e8fd503d748c7d974c3e48da8f41350fa5222ef9b4391" 2163 + dependencies = [ 2164 + "atomic-waker", 2165 + "fastrand 2.1.0", 2166 + "futures-io", 2167 + ] 2168 + 2169 + [[package]] 2170 + name = "pkg-config" 2171 + version = "0.3.30" 2172 + source = "registry+https://github.com/rust-lang/crates.io-index" 2173 + checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" 2174 + 2175 + [[package]] 2176 + name = "polling" 2177 + version = "2.8.0" 2178 + source = "registry+https://github.com/rust-lang/crates.io-index" 2179 + checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" 2180 + dependencies = [ 2181 + "autocfg", 2182 + "bitflags 1.3.2", 2183 + "cfg-if", 2184 + "concurrent-queue", 2185 + "libc", 2186 + "log", 2187 + "pin-project-lite", 2188 + "windows-sys 0.48.0", 2189 + ] 2190 + 2191 + [[package]] 2192 + name = "polling" 2193 + version = "3.7.2" 2194 + source = "registry+https://github.com/rust-lang/crates.io-index" 2195 + checksum = "a3ed00ed3fbf728b5816498ecd316d1716eecaced9c0c8d2c5a6740ca214985b" 2196 + dependencies = [ 2197 + "cfg-if", 2198 + "concurrent-queue", 2199 + "hermit-abi 0.4.0", 2200 + "pin-project-lite", 2201 + "rustix 0.38.34", 2202 + "tracing", 2203 + "windows-sys 0.52.0", 2204 + ] 2205 + 2206 + [[package]] 2207 + name = "polyval" 2208 + version = "0.6.2" 2209 + source = "registry+https://github.com/rust-lang/crates.io-index" 2210 + checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" 2211 + dependencies = [ 2212 + "cfg-if", 2213 + "cpufeatures", 2214 + "opaque-debug", 2215 + "universal-hash", 2216 + ] 2217 + 2218 + [[package]] 2219 + name = "portable-atomic" 2220 + version = "1.6.0" 2221 + source = "registry+https://github.com/rust-lang/crates.io-index" 2222 + checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" 2223 + 2224 + [[package]] 2225 + name = "postgres" 2226 + version = "0.19.9" 2227 + source = "registry+https://github.com/rust-lang/crates.io-index" 2228 + checksum = "95c918733159f4d55d2ceb262950f00b0aebd6af4aa97b5a47bb0655120475ed" 2229 + dependencies = [ 2230 + "bytes", 2231 + "fallible-iterator 0.2.0", 2232 + "futures-util", 2233 + "log", 2234 + "tokio", 2235 + "tokio-postgres", 2236 + ] 2237 + 2238 + [[package]] 2239 + name = "postgres-protocol" 2240 + version = "0.6.7" 2241 + source = "registry+https://github.com/rust-lang/crates.io-index" 2242 + checksum = "acda0ebdebc28befa84bee35e651e4c5f09073d668c7aed4cf7e23c3cda84b23" 2243 + dependencies = [ 2244 + "base64 0.22.1", 2245 + "byteorder", 2246 + "bytes", 2247 + "fallible-iterator 0.2.0", 2248 + "hmac", 2249 + "md-5", 2250 + "memchr", 2251 + "rand", 2252 + "sha2", 2253 + "stringprep", 2254 + ] 2255 + 2256 + [[package]] 2257 + name = "postgres-types" 2258 + version = "0.2.8" 2259 + source = "registry+https://github.com/rust-lang/crates.io-index" 2260 + checksum = "f66ea23a2d0e5734297357705193335e0a957696f34bed2f2faefacb2fec336f" 2261 + dependencies = [ 2262 + "bytes", 2263 + "fallible-iterator 0.2.0", 2264 + "postgres-protocol", 2265 + ] 2266 + 2267 + [[package]] 2268 + name = "powerfmt" 2269 + version = "0.2.0" 2270 + source = "registry+https://github.com/rust-lang/crates.io-index" 2271 + checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" 2272 + 2273 + [[package]] 2274 + name = "ppv-lite86" 2275 + version = "0.2.17" 2276 + source = "registry+https://github.com/rust-lang/crates.io-index" 2277 + checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" 2278 + 2279 + [[package]] 2280 + name = "precomputed-hash" 2281 + version = "0.1.1" 2282 + source = "registry+https://github.com/rust-lang/crates.io-index" 2283 + checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" 2284 + 2285 + [[package]] 2286 + name = "proc-macro2" 2287 + version = "1.0.86" 2288 + source = "registry+https://github.com/rust-lang/crates.io-index" 2289 + checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" 2290 + dependencies = [ 2291 + "unicode-ident", 2292 + ] 2293 + 2294 + [[package]] 2295 + name = "quote" 2296 + version = "1.0.36" 2297 + source = "registry+https://github.com/rust-lang/crates.io-index" 2298 + checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" 2299 + dependencies = [ 2300 + "proc-macro2", 2301 + ] 2302 + 2303 + [[package]] 2304 + name = "rand" 2305 + version = "0.8.5" 2306 + source = "registry+https://github.com/rust-lang/crates.io-index" 2307 + checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 2308 + dependencies = [ 2309 + "libc", 2310 + "rand_chacha", 2311 + "rand_core", 2312 + ] 2313 + 2314 + [[package]] 2315 + name = "rand_chacha" 2316 + version = "0.3.1" 2317 + source = "registry+https://github.com/rust-lang/crates.io-index" 2318 + checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 2319 + dependencies = [ 2320 + "ppv-lite86", 2321 + "rand_core", 2322 + ] 2323 + 2324 + [[package]] 2325 + name = "rand_core" 2326 + version = "0.6.4" 2327 + source = "registry+https://github.com/rust-lang/crates.io-index" 2328 + checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 2329 + dependencies = [ 2330 + "getrandom", 2331 + ] 2332 + 2333 + [[package]] 2334 + name = "redox_syscall" 2335 + version = "0.5.2" 2336 + source = "registry+https://github.com/rust-lang/crates.io-index" 2337 + checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" 2338 + dependencies = [ 2339 + "bitflags 2.6.0", 2340 + ] 2341 + 2342 + [[package]] 2343 + name = "regex" 2344 + version = "1.10.5" 2345 + source = "registry+https://github.com/rust-lang/crates.io-index" 2346 + checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" 2347 + dependencies = [ 2348 + "aho-corasick", 2349 + "memchr", 2350 + "regex-automata", 2351 + "regex-syntax", 2352 + ] 2353 + 2354 + [[package]] 2355 + name = "regex-automata" 2356 + version = "0.4.7" 2357 + source = "registry+https://github.com/rust-lang/crates.io-index" 2358 + checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" 2359 + dependencies = [ 2360 + "aho-corasick", 2361 + "memchr", 2362 + "regex-syntax", 2363 + ] 2364 + 2365 + [[package]] 2366 + name = "regex-lite" 2367 + version = "0.1.6" 2368 + source = "registry+https://github.com/rust-lang/crates.io-index" 2369 + checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" 2370 + 2371 + [[package]] 2372 + name = "regex-syntax" 2373 + version = "0.8.4" 2374 + source = "registry+https://github.com/rust-lang/crates.io-index" 2375 + checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" 2376 + 2377 + [[package]] 2378 + name = "reqwest" 2379 + version = "0.12.5" 2380 + source = "registry+https://github.com/rust-lang/crates.io-index" 2381 + checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37" 2382 + dependencies = [ 2383 + "base64 0.22.1", 2384 + "bytes", 2385 + "encoding_rs", 2386 + "futures-channel", 2387 + "futures-core", 2388 + "futures-util", 2389 + "h2 0.4.5", 2390 + "http 1.1.0", 2391 + "http-body", 2392 + "http-body-util", 2393 + "hyper", 2394 + "hyper-rustls", 2395 + "hyper-tls", 2396 + "hyper-util", 2397 + "ipnet", 2398 + "js-sys", 2399 + "log", 2400 + "mime", 2401 + "native-tls", 2402 + "once_cell", 2403 + "percent-encoding", 2404 + "pin-project-lite", 2405 + "rustls-pemfile", 2406 + "serde", 2407 + "serde_json", 2408 + "serde_urlencoded", 2409 + "sync_wrapper", 2410 + "system-configuration", 2411 + "tokio", 2412 + "tokio-native-tls", 2413 + "tower-service", 2414 + "url", 2415 + "wasm-bindgen", 2416 + "wasm-bindgen-futures", 2417 + "web-sys", 2418 + "winreg", 2419 + ] 2420 + 2421 + [[package]] 2422 + name = "ring" 2423 + version = "0.17.8" 2424 + source = "registry+https://github.com/rust-lang/crates.io-index" 2425 + checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" 2426 + dependencies = [ 2427 + "cc", 2428 + "cfg-if", 2429 + "getrandom", 2430 + "libc", 2431 + "spin", 2432 + "untrusted", 2433 + "windows-sys 0.52.0", 2434 + ] 2435 + 2436 + [[package]] 2437 + name = "rusqlite" 2438 + version = "0.32.1" 2439 + source = "registry+https://github.com/rust-lang/crates.io-index" 2440 + checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e" 2441 + dependencies = [ 2442 + "bitflags 2.6.0", 2443 + "fallible-iterator 0.3.0", 2444 + "fallible-streaming-iterator", 2445 + "hashlink", 2446 + "libsqlite3-sys", 2447 + "smallvec", 2448 + ] 2449 + 2450 + [[package]] 2451 + name = "rustc-demangle" 2452 + version = "0.1.24" 2453 + source = "registry+https://github.com/rust-lang/crates.io-index" 2454 + checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" 2455 + 2456 + [[package]] 2457 + name = "rustc_version" 2458 + version = "0.4.0" 2459 + source = "registry+https://github.com/rust-lang/crates.io-index" 2460 + checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" 2461 + dependencies = [ 2462 + "semver", 2463 + ] 2464 + 2465 + [[package]] 2466 + name = "rustix" 2467 + version = "0.37.27" 2468 + source = "registry+https://github.com/rust-lang/crates.io-index" 2469 + checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" 2470 + dependencies = [ 2471 + "bitflags 1.3.2", 2472 + "errno", 2473 + "io-lifetimes", 2474 + "libc", 2475 + "linux-raw-sys 0.3.8", 2476 + "windows-sys 0.48.0", 2477 + ] 2478 + 2479 + [[package]] 2480 + name = "rustix" 2481 + version = "0.38.34" 2482 + source = "registry+https://github.com/rust-lang/crates.io-index" 2483 + checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" 2484 + dependencies = [ 2485 + "bitflags 2.6.0", 2486 + "errno", 2487 + "libc", 2488 + "linux-raw-sys 0.4.14", 2489 + "windows-sys 0.52.0", 2490 + ] 2491 + 2492 + [[package]] 2493 + name = "rustls" 2494 + version = "0.23.10" 2495 + source = "registry+https://github.com/rust-lang/crates.io-index" 2496 + checksum = "05cff451f60db80f490f3c182b77c35260baace73209e9cdbbe526bfe3a4d402" 2497 + dependencies = [ 2498 + "once_cell", 2499 + "rustls-pki-types", 2500 + "rustls-webpki", 2501 + "subtle", 2502 + "zeroize", 2503 + ] 2504 + 2505 + [[package]] 2506 + name = "rustls-pemfile" 2507 + version = "2.1.2" 2508 + source = "registry+https://github.com/rust-lang/crates.io-index" 2509 + checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" 2510 + dependencies = [ 2511 + "base64 0.22.1", 2512 + "rustls-pki-types", 2513 + ] 2514 + 2515 + [[package]] 2516 + name = "rustls-pki-types" 2517 + version = "1.7.0" 2518 + source = "registry+https://github.com/rust-lang/crates.io-index" 2519 + checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" 2520 + 2521 + [[package]] 2522 + name = "rustls-webpki" 2523 + version = "0.102.4" 2524 + source = "registry+https://github.com/rust-lang/crates.io-index" 2525 + checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e" 2526 + dependencies = [ 2527 + "ring", 2528 + "rustls-pki-types", 2529 + "untrusted", 2530 + ] 2531 + 2532 + [[package]] 2533 + name = "ryu" 2534 + version = "1.0.18" 2535 + source = "registry+https://github.com/rust-lang/crates.io-index" 2536 + checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" 2537 + 2538 + [[package]] 2539 + name = "schannel" 2540 + version = "0.1.23" 2541 + source = "registry+https://github.com/rust-lang/crates.io-index" 2542 + checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" 2543 + dependencies = [ 2544 + "windows-sys 0.52.0", 2545 + ] 2546 + 2547 + [[package]] 2548 + name = "scopeguard" 2549 + version = "1.2.0" 2550 + source = "registry+https://github.com/rust-lang/crates.io-index" 2551 + checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 2552 + 2553 + [[package]] 2554 + name = "scraper" 2555 + version = "0.19.0" 2556 + source = "registry+https://github.com/rust-lang/crates.io-index" 2557 + checksum = "5b80b33679ff7a0ea53d37f3b39de77ea0c75b12c5805ac43ec0c33b3051af1b" 2558 + dependencies = [ 2559 + "ahash", 2560 + "cssparser", 2561 + "ego-tree", 2562 + "getopts", 2563 + "html5ever", 2564 + "once_cell", 2565 + "selectors", 2566 + "tendril", 2567 + ] 2568 + 2569 + [[package]] 2570 + name = "security-framework" 2571 + version = "2.11.0" 2572 + source = "registry+https://github.com/rust-lang/crates.io-index" 2573 + checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" 2574 + dependencies = [ 2575 + "bitflags 2.6.0", 2576 + "core-foundation", 2577 + "core-foundation-sys", 2578 + "libc", 2579 + "security-framework-sys", 2580 + ] 2581 + 2582 + [[package]] 2583 + name = "security-framework-sys" 2584 + version = "2.11.0" 2585 + source = "registry+https://github.com/rust-lang/crates.io-index" 2586 + checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" 2587 + dependencies = [ 2588 + "core-foundation-sys", 2589 + "libc", 2590 + ] 2591 + 2592 + [[package]] 2593 + name = "selectors" 2594 + version = "0.25.0" 2595 + source = "registry+https://github.com/rust-lang/crates.io-index" 2596 + checksum = "4eb30575f3638fc8f6815f448d50cb1a2e255b0897985c8c59f4d37b72a07b06" 2597 + dependencies = [ 2598 + "bitflags 2.6.0", 2599 + "cssparser", 2600 + "derive_more 0.99.18", 2601 + "fxhash", 2602 + "log", 2603 + "new_debug_unreachable", 2604 + "phf 0.10.1", 2605 + "phf_codegen", 2606 + "precomputed-hash", 2607 + "servo_arc", 2608 + "smallvec", 2609 + ] 2610 + 2611 + [[package]] 2612 + name = "semver" 2613 + version = "1.0.23" 2614 + source = "registry+https://github.com/rust-lang/crates.io-index" 2615 + checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" 2616 + 2617 + [[package]] 2618 + name = "serde" 2619 + version = "1.0.204" 2620 + source = "registry+https://github.com/rust-lang/crates.io-index" 2621 + checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" 2622 + dependencies = [ 2623 + "serde_derive", 2624 + ] 2625 + 2626 + [[package]] 2627 + name = "serde_derive" 2628 + version = "1.0.204" 2629 + source = "registry+https://github.com/rust-lang/crates.io-index" 2630 + checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" 2631 + dependencies = [ 2632 + "proc-macro2", 2633 + "quote", 2634 + "syn 2.0.68", 2635 + ] 2636 + 2637 + [[package]] 2638 + name = "serde_json" 2639 + version = "1.0.120" 2640 + source = "registry+https://github.com/rust-lang/crates.io-index" 2641 + checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" 2642 + dependencies = [ 2643 + "itoa", 2644 + "ryu", 2645 + "serde", 2646 + ] 2647 + 2648 + [[package]] 2649 + name = "serde_spanned" 2650 + version = "0.6.6" 2651 + source = "registry+https://github.com/rust-lang/crates.io-index" 2652 + checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" 2653 + dependencies = [ 2654 + "serde", 2655 + ] 2656 + 2657 + [[package]] 2658 + name = "serde_urlencoded" 2659 + version = "0.7.1" 2660 + source = "registry+https://github.com/rust-lang/crates.io-index" 2661 + checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" 2662 + dependencies = [ 2663 + "form_urlencoded", 2664 + "itoa", 2665 + "ryu", 2666 + "serde", 2667 + ] 2668 + 2669 + [[package]] 2670 + name = "servo_arc" 2671 + version = "0.3.0" 2672 + source = "registry+https://github.com/rust-lang/crates.io-index" 2673 + checksum = "d036d71a959e00c77a63538b90a6c2390969f9772b096ea837205c6bd0491a44" 2674 + dependencies = [ 2675 + "stable_deref_trait", 2676 + ] 2677 + 2678 + [[package]] 2679 + name = "sha1" 2680 + version = "0.10.6" 2681 + source = "registry+https://github.com/rust-lang/crates.io-index" 2682 + checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" 2683 + dependencies = [ 2684 + "cfg-if", 2685 + "cpufeatures", 2686 + "digest", 2687 + ] 2688 + 2689 + [[package]] 2690 + name = "sha2" 2691 + version = "0.10.8" 2692 + source = "registry+https://github.com/rust-lang/crates.io-index" 2693 + checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" 2694 + dependencies = [ 2695 + "cfg-if", 2696 + "cpufeatures", 2697 + "digest", 2698 + ] 2699 + 2700 + [[package]] 2701 + name = "shlex" 2702 + version = "1.3.0" 2703 + source = "registry+https://github.com/rust-lang/crates.io-index" 2704 + checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 2705 + 2706 + [[package]] 2707 + name = "signal-hook-registry" 2708 + version = "1.4.2" 2709 + source = "registry+https://github.com/rust-lang/crates.io-index" 2710 + checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" 2711 + dependencies = [ 2712 + "libc", 2713 + ] 2714 + 2715 + [[package]] 2716 + name = "simplelog" 2717 + version = "0.12.2" 2718 + source = "registry+https://github.com/rust-lang/crates.io-index" 2719 + checksum = "16257adbfaef1ee58b1363bdc0664c9b8e1e30aed86049635fb5f147d065a9c0" 2720 + dependencies = [ 2721 + "log", 2722 + "termcolor", 2723 + "time", 2724 + ] 2725 + 2726 + [[package]] 2727 + name = "siphasher" 2728 + version = "0.3.11" 2729 + source = "registry+https://github.com/rust-lang/crates.io-index" 2730 + checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" 2731 + 2732 + [[package]] 2733 + name = "slab" 2734 + version = "0.4.9" 2735 + source = "registry+https://github.com/rust-lang/crates.io-index" 2736 + checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" 2737 + dependencies = [ 2738 + "autocfg", 2739 + ] 2740 + 2741 + [[package]] 2742 + name = "smallvec" 2743 + version = "1.13.2" 2744 + source = "registry+https://github.com/rust-lang/crates.io-index" 2745 + checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" 2746 + 2747 + [[package]] 2748 + name = "socket2" 2749 + version = "0.4.10" 2750 + source = "registry+https://github.com/rust-lang/crates.io-index" 2751 + checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" 2752 + dependencies = [ 2753 + "libc", 2754 + "winapi", 2755 + ] 2756 + 2757 + [[package]] 2758 + name = "socket2" 2759 + version = "0.5.7" 2760 + source = "registry+https://github.com/rust-lang/crates.io-index" 2761 + checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" 2762 + dependencies = [ 2763 + "libc", 2764 + "windows-sys 0.52.0", 2765 + ] 2766 + 2767 + [[package]] 2768 + name = "spin" 2769 + version = "0.9.8" 2770 + source = "registry+https://github.com/rust-lang/crates.io-index" 2771 + checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" 2772 + 2773 + [[package]] 2774 + name = "stable_deref_trait" 2775 + version = "1.2.0" 2776 + source = "registry+https://github.com/rust-lang/crates.io-index" 2777 + checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 2778 + 2779 + [[package]] 2780 + name = "strawmelonjuice-lumina" 2781 + version = "0.1.0" 2782 + dependencies = [ 2783 + "actix-identity", 2784 + "actix-session", 2785 + "actix-web", 2786 + "async-std", 2787 + "build_const", 2788 + "chrono", 2789 + "colored", 2790 + "console", 2791 + "dotenv", 2792 + "futures", 2793 + "handlebars", 2794 + "home", 2795 + "indicatif", 2796 + "log", 2797 + "lumina-urls", 2798 + "magic-crypt", 2799 + "markdown", 2800 + "password-hash", 2801 + "pbkdf2", 2802 + "postgres", 2803 + "rand", 2804 + "regex", 2805 + "reqwest", 2806 + "rusqlite", 2807 + "serde", 2808 + "serde_json", 2809 + "simplelog", 2810 + "time", 2811 + "tokio", 2812 + "toml", 2813 + ] 2814 + 2815 + [[package]] 2816 + name = "string_cache" 2817 + version = "0.8.7" 2818 + source = "registry+https://github.com/rust-lang/crates.io-index" 2819 + checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" 2820 + dependencies = [ 2821 + "new_debug_unreachable", 2822 + "once_cell", 2823 + "parking_lot", 2824 + "phf_shared 0.10.0", 2825 + "precomputed-hash", 2826 + "serde", 2827 + ] 2828 + 2829 + [[package]] 2830 + name = "string_cache_codegen" 2831 + version = "0.5.2" 2832 + source = "registry+https://github.com/rust-lang/crates.io-index" 2833 + checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" 2834 + dependencies = [ 2835 + "phf_generator 0.10.0", 2836 + "phf_shared 0.10.0", 2837 + "proc-macro2", 2838 + "quote", 2839 + ] 2840 + 2841 + [[package]] 2842 + name = "stringprep" 2843 + version = "0.1.5" 2844 + source = "registry+https://github.com/rust-lang/crates.io-index" 2845 + checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" 2846 + dependencies = [ 2847 + "unicode-bidi", 2848 + "unicode-normalization", 2849 + "unicode-properties", 2850 + ] 2851 + 2852 + [[package]] 2853 + name = "subtle" 2854 + version = "2.6.1" 2855 + source = "registry+https://github.com/rust-lang/crates.io-index" 2856 + checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" 2857 + 2858 + [[package]] 2859 + name = "syn" 2860 + version = "1.0.109" 2861 + source = "registry+https://github.com/rust-lang/crates.io-index" 2862 + checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 2863 + dependencies = [ 2864 + "proc-macro2", 2865 + "quote", 2866 + "unicode-ident", 2867 + ] 2868 + 2869 + [[package]] 2870 + name = "syn" 2871 + version = "2.0.68" 2872 + source = "registry+https://github.com/rust-lang/crates.io-index" 2873 + checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" 2874 + dependencies = [ 2875 + "proc-macro2", 2876 + "quote", 2877 + "unicode-ident", 2878 + ] 2879 + 2880 + [[package]] 2881 + name = "sync_wrapper" 2882 + version = "1.0.1" 2883 + source = "registry+https://github.com/rust-lang/crates.io-index" 2884 + checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" 2885 + 2886 + [[package]] 2887 + name = "system-configuration" 2888 + version = "0.5.1" 2889 + source = "registry+https://github.com/rust-lang/crates.io-index" 2890 + checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" 2891 + dependencies = [ 2892 + "bitflags 1.3.2", 2893 + "core-foundation", 2894 + "system-configuration-sys", 2895 + ] 2896 + 2897 + [[package]] 2898 + name = "system-configuration-sys" 2899 + version = "0.5.0" 2900 + source = "registry+https://github.com/rust-lang/crates.io-index" 2901 + checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" 2902 + dependencies = [ 2903 + "core-foundation-sys", 2904 + "libc", 2905 + ] 2906 + 2907 + [[package]] 2908 + name = "tempfile" 2909 + version = "3.10.1" 2910 + source = "registry+https://github.com/rust-lang/crates.io-index" 2911 + checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" 2912 + dependencies = [ 2913 + "cfg-if", 2914 + "fastrand 2.1.0", 2915 + "rustix 0.38.34", 2916 + "windows-sys 0.52.0", 2917 + ] 2918 + 2919 + [[package]] 2920 + name = "tendril" 2921 + version = "0.4.3" 2922 + source = "registry+https://github.com/rust-lang/crates.io-index" 2923 + checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" 2924 + dependencies = [ 2925 + "futf", 2926 + "mac", 2927 + "utf-8", 2928 + ] 2929 + 2930 + [[package]] 2931 + name = "termcolor" 2932 + version = "1.4.1" 2933 + source = "registry+https://github.com/rust-lang/crates.io-index" 2934 + checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" 2935 + dependencies = [ 2936 + "winapi-util", 2937 + ] 2938 + 2939 + [[package]] 2940 + name = "thiserror" 2941 + version = "1.0.61" 2942 + source = "registry+https://github.com/rust-lang/crates.io-index" 2943 + checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" 2944 + dependencies = [ 2945 + "thiserror-impl", 2946 + ] 2947 + 2948 + [[package]] 2949 + name = "thiserror-impl" 2950 + version = "1.0.61" 2951 + source = "registry+https://github.com/rust-lang/crates.io-index" 2952 + checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" 2953 + dependencies = [ 2954 + "proc-macro2", 2955 + "quote", 2956 + "syn 2.0.68", 2957 + ] 2958 + 2959 + [[package]] 2960 + name = "tiger" 2961 + version = "0.2.1" 2962 + source = "registry+https://github.com/rust-lang/crates.io-index" 2963 + checksum = "579abbce4ad73b04386dbeb34369c9873a8f9b749c7b99cbf479a2949ff715ed" 2964 + dependencies = [ 2965 + "digest", 2966 + ] 2967 + 2968 + [[package]] 2969 + name = "time" 2970 + version = "0.3.36" 2971 + source = "registry+https://github.com/rust-lang/crates.io-index" 2972 + checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" 2973 + dependencies = [ 2974 + "deranged", 2975 + "itoa", 2976 + "libc", 2977 + "num-conv", 2978 + "num_threads", 2979 + "powerfmt", 2980 + "serde", 2981 + "time-core", 2982 + "time-macros", 2983 + ] 2984 + 2985 + [[package]] 2986 + name = "time-core" 2987 + version = "0.1.2" 2988 + source = "registry+https://github.com/rust-lang/crates.io-index" 2989 + checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" 2990 + 2991 + [[package]] 2992 + name = "time-macros" 2993 + version = "0.2.18" 2994 + source = "registry+https://github.com/rust-lang/crates.io-index" 2995 + checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" 2996 + dependencies = [ 2997 + "num-conv", 2998 + "time-core", 2999 + ] 3000 + 3001 + [[package]] 3002 + name = "tinyvec" 3003 + version = "1.6.1" 3004 + source = "registry+https://github.com/rust-lang/crates.io-index" 3005 + checksum = "c55115c6fbe2d2bef26eb09ad74bde02d8255476fc0c7b515ef09fbb35742d82" 3006 + dependencies = [ 3007 + "tinyvec_macros", 3008 + ] 3009 + 3010 + [[package]] 3011 + name = "tinyvec_macros" 3012 + version = "0.1.1" 3013 + source = "registry+https://github.com/rust-lang/crates.io-index" 3014 + checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" 3015 + 3016 + [[package]] 3017 + name = "tokio" 3018 + version = "1.38.0" 3019 + source = "registry+https://github.com/rust-lang/crates.io-index" 3020 + checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" 3021 + dependencies = [ 3022 + "backtrace", 3023 + "bytes", 3024 + "libc", 3025 + "mio", 3026 + "num_cpus", 3027 + "parking_lot", 3028 + "pin-project-lite", 3029 + "signal-hook-registry", 3030 + "socket2 0.5.7", 3031 + "tokio-macros", 3032 + "windows-sys 0.48.0", 3033 + ] 3034 + 3035 + [[package]] 3036 + name = "tokio-macros" 3037 + version = "2.3.0" 3038 + source = "registry+https://github.com/rust-lang/crates.io-index" 3039 + checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" 3040 + dependencies = [ 3041 + "proc-macro2", 3042 + "quote", 3043 + "syn 2.0.68", 3044 + ] 3045 + 3046 + [[package]] 3047 + name = "tokio-native-tls" 3048 + version = "0.3.1" 3049 + source = "registry+https://github.com/rust-lang/crates.io-index" 3050 + checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" 3051 + dependencies = [ 3052 + "native-tls", 3053 + "tokio", 3054 + ] 3055 + 3056 + [[package]] 3057 + name = "tokio-postgres" 3058 + version = "0.7.12" 3059 + source = "registry+https://github.com/rust-lang/crates.io-index" 3060 + checksum = "3b5d3742945bc7d7f210693b0c58ae542c6fd47b17adbbda0885f3dcb34a6bdb" 3061 + dependencies = [ 3062 + "async-trait", 3063 + "byteorder", 3064 + "bytes", 3065 + "fallible-iterator 0.2.0", 3066 + "futures-channel", 3067 + "futures-util", 3068 + "log", 3069 + "parking_lot", 3070 + "percent-encoding", 3071 + "phf 0.11.2", 3072 + "pin-project-lite", 3073 + "postgres-protocol", 3074 + "postgres-types", 3075 + "rand", 3076 + "socket2 0.5.7", 3077 + "tokio", 3078 + "tokio-util", 3079 + "whoami", 3080 + ] 3081 + 3082 + [[package]] 3083 + name = "tokio-rustls" 3084 + version = "0.26.0" 3085 + source = "registry+https://github.com/rust-lang/crates.io-index" 3086 + checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" 3087 + dependencies = [ 3088 + "rustls", 3089 + "rustls-pki-types", 3090 + "tokio", 3091 + ] 3092 + 3093 + [[package]] 3094 + name = "tokio-util" 3095 + version = "0.7.11" 3096 + source = "registry+https://github.com/rust-lang/crates.io-index" 3097 + checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" 3098 + dependencies = [ 3099 + "bytes", 3100 + "futures-core", 3101 + "futures-sink", 3102 + "pin-project-lite", 3103 + "tokio", 3104 + ] 3105 + 3106 + [[package]] 3107 + name = "toml" 3108 + version = "0.8.14" 3109 + source = "registry+https://github.com/rust-lang/crates.io-index" 3110 + checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" 3111 + dependencies = [ 3112 + "serde", 3113 + "serde_spanned", 3114 + "toml_datetime", 3115 + "toml_edit", 3116 + ] 3117 + 3118 + [[package]] 3119 + name = "toml_datetime" 3120 + version = "0.6.6" 3121 + source = "registry+https://github.com/rust-lang/crates.io-index" 3122 + checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" 3123 + dependencies = [ 3124 + "serde", 3125 + ] 3126 + 3127 + [[package]] 3128 + name = "toml_edit" 3129 + version = "0.22.14" 3130 + source = "registry+https://github.com/rust-lang/crates.io-index" 3131 + checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38" 3132 + dependencies = [ 3133 + "indexmap", 3134 + "serde", 3135 + "serde_spanned", 3136 + "toml_datetime", 3137 + "winnow", 3138 + ] 3139 + 3140 + [[package]] 3141 + name = "tower" 3142 + version = "0.4.13" 3143 + source = "registry+https://github.com/rust-lang/crates.io-index" 3144 + checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" 3145 + dependencies = [ 3146 + "futures-core", 3147 + "futures-util", 3148 + "pin-project", 3149 + "pin-project-lite", 3150 + "tokio", 3151 + "tower-layer", 3152 + "tower-service", 3153 + ] 3154 + 3155 + [[package]] 3156 + name = "tower-layer" 3157 + version = "0.3.2" 3158 + source = "registry+https://github.com/rust-lang/crates.io-index" 3159 + checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" 3160 + 3161 + [[package]] 3162 + name = "tower-service" 3163 + version = "0.3.2" 3164 + source = "registry+https://github.com/rust-lang/crates.io-index" 3165 + checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" 3166 + 3167 + [[package]] 3168 + name = "tracing" 3169 + version = "0.1.40" 3170 + source = "registry+https://github.com/rust-lang/crates.io-index" 3171 + checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" 3172 + dependencies = [ 3173 + "log", 3174 + "pin-project-lite", 3175 + "tracing-core", 3176 + ] 3177 + 3178 + [[package]] 3179 + name = "tracing-core" 3180 + version = "0.1.32" 3181 + source = "registry+https://github.com/rust-lang/crates.io-index" 3182 + checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" 3183 + dependencies = [ 3184 + "once_cell", 3185 + ] 3186 + 3187 + [[package]] 3188 + name = "try-lock" 3189 + version = "0.2.5" 3190 + source = "registry+https://github.com/rust-lang/crates.io-index" 3191 + checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" 3192 + 3193 + [[package]] 3194 + name = "typenum" 3195 + version = "1.17.0" 3196 + source = "registry+https://github.com/rust-lang/crates.io-index" 3197 + checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" 3198 + 3199 + [[package]] 3200 + name = "ucd-trie" 3201 + version = "0.1.6" 3202 + source = "registry+https://github.com/rust-lang/crates.io-index" 3203 + checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" 3204 + 3205 + [[package]] 3206 + name = "unicode-bidi" 3207 + version = "0.3.15" 3208 + source = "registry+https://github.com/rust-lang/crates.io-index" 3209 + checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" 3210 + 3211 + [[package]] 3212 + name = "unicode-id" 3213 + version = "0.3.4" 3214 + source = "registry+https://github.com/rust-lang/crates.io-index" 3215 + checksum = "b1b6def86329695390197b82c1e244a54a131ceb66c996f2088a3876e2ae083f" 3216 + 3217 + [[package]] 3218 + name = "unicode-ident" 3219 + version = "1.0.12" 3220 + source = "registry+https://github.com/rust-lang/crates.io-index" 3221 + checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 3222 + 3223 + [[package]] 3224 + name = "unicode-normalization" 3225 + version = "0.1.23" 3226 + source = "registry+https://github.com/rust-lang/crates.io-index" 3227 + checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" 3228 + dependencies = [ 3229 + "tinyvec", 3230 + ] 3231 + 3232 + [[package]] 3233 + name = "unicode-properties" 3234 + version = "0.1.3" 3235 + source = "registry+https://github.com/rust-lang/crates.io-index" 3236 + checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" 3237 + 3238 + [[package]] 3239 + name = "unicode-width" 3240 + version = "0.1.13" 3241 + source = "registry+https://github.com/rust-lang/crates.io-index" 3242 + checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" 3243 + 3244 + [[package]] 3245 + name = "unicode-xid" 3246 + version = "0.2.6" 3247 + source = "registry+https://github.com/rust-lang/crates.io-index" 3248 + checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" 3249 + 3250 + [[package]] 3251 + name = "universal-hash" 3252 + version = "0.5.1" 3253 + source = "registry+https://github.com/rust-lang/crates.io-index" 3254 + checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" 3255 + dependencies = [ 3256 + "crypto-common", 3257 + "subtle", 3258 + ] 3259 + 3260 + [[package]] 3261 + name = "untrusted" 3262 + version = "0.9.0" 3263 + source = "registry+https://github.com/rust-lang/crates.io-index" 3264 + checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" 3265 + 3266 + [[package]] 3267 + name = "url" 3268 + version = "2.5.2" 3269 + source = "registry+https://github.com/rust-lang/crates.io-index" 3270 + checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" 3271 + dependencies = [ 3272 + "form_urlencoded", 3273 + "idna", 3274 + "percent-encoding", 3275 + ] 3276 + 3277 + [[package]] 3278 + name = "utf-8" 3279 + version = "0.7.6" 3280 + source = "registry+https://github.com/rust-lang/crates.io-index" 3281 + checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" 3282 + 3283 + [[package]] 3284 + name = "value-bag" 3285 + version = "1.9.0" 3286 + source = "registry+https://github.com/rust-lang/crates.io-index" 3287 + checksum = "5a84c137d37ab0142f0f2ddfe332651fdbf252e7b7dbb4e67b6c1f1b2e925101" 3288 + 3289 + [[package]] 3290 + name = "vcpkg" 3291 + version = "0.2.15" 3292 + source = "registry+https://github.com/rust-lang/crates.io-index" 3293 + checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" 3294 + 3295 + [[package]] 3296 + name = "version_check" 3297 + version = "0.9.4" 3298 + source = "registry+https://github.com/rust-lang/crates.io-index" 3299 + checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 3300 + 3301 + [[package]] 3302 + name = "waker-fn" 3303 + version = "1.2.0" 3304 + source = "registry+https://github.com/rust-lang/crates.io-index" 3305 + checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" 3306 + 3307 + [[package]] 3308 + name = "want" 3309 + version = "0.3.1" 3310 + source = "registry+https://github.com/rust-lang/crates.io-index" 3311 + checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" 3312 + dependencies = [ 3313 + "try-lock", 3314 + ] 3315 + 3316 + [[package]] 3317 + name = "wasi" 3318 + version = "0.11.0+wasi-snapshot-preview1" 3319 + source = "registry+https://github.com/rust-lang/crates.io-index" 3320 + checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 3321 + 3322 + [[package]] 3323 + name = "wasite" 3324 + version = "0.1.0" 3325 + source = "registry+https://github.com/rust-lang/crates.io-index" 3326 + checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" 3327 + 3328 + [[package]] 3329 + name = "wasm-bindgen" 3330 + version = "0.2.92" 3331 + source = "registry+https://github.com/rust-lang/crates.io-index" 3332 + checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" 3333 + dependencies = [ 3334 + "cfg-if", 3335 + "wasm-bindgen-macro", 3336 + ] 3337 + 3338 + [[package]] 3339 + name = "wasm-bindgen-backend" 3340 + version = "0.2.92" 3341 + source = "registry+https://github.com/rust-lang/crates.io-index" 3342 + checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" 3343 + dependencies = [ 3344 + "bumpalo", 3345 + "log", 3346 + "once_cell", 3347 + "proc-macro2", 3348 + "quote", 3349 + "syn 2.0.68", 3350 + "wasm-bindgen-shared", 3351 + ] 3352 + 3353 + [[package]] 3354 + name = "wasm-bindgen-futures" 3355 + version = "0.4.42" 3356 + source = "registry+https://github.com/rust-lang/crates.io-index" 3357 + checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" 3358 + dependencies = [ 3359 + "cfg-if", 3360 + "js-sys", 3361 + "wasm-bindgen", 3362 + "web-sys", 3363 + ] 3364 + 3365 + [[package]] 3366 + name = "wasm-bindgen-macro" 3367 + version = "0.2.92" 3368 + source = "registry+https://github.com/rust-lang/crates.io-index" 3369 + checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" 3370 + dependencies = [ 3371 + "quote", 3372 + "wasm-bindgen-macro-support", 3373 + ] 3374 + 3375 + [[package]] 3376 + name = "wasm-bindgen-macro-support" 3377 + version = "0.2.92" 3378 + source = "registry+https://github.com/rust-lang/crates.io-index" 3379 + checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" 3380 + dependencies = [ 3381 + "proc-macro2", 3382 + "quote", 3383 + "syn 2.0.68", 3384 + "wasm-bindgen-backend", 3385 + "wasm-bindgen-shared", 3386 + ] 3387 + 3388 + [[package]] 3389 + name = "wasm-bindgen-shared" 3390 + version = "0.2.92" 3391 + source = "registry+https://github.com/rust-lang/crates.io-index" 3392 + checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" 3393 + 3394 + [[package]] 3395 + name = "web-sys" 3396 + version = "0.3.69" 3397 + source = "registry+https://github.com/rust-lang/crates.io-index" 3398 + checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" 3399 + dependencies = [ 3400 + "js-sys", 3401 + "wasm-bindgen", 3402 + ] 3403 + 3404 + [[package]] 3405 + name = "whoami" 3406 + version = "1.5.2" 3407 + source = "registry+https://github.com/rust-lang/crates.io-index" 3408 + checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d" 3409 + dependencies = [ 3410 + "redox_syscall", 3411 + "wasite", 3412 + "web-sys", 3413 + ] 3414 + 3415 + [[package]] 3416 + name = "winapi" 3417 + version = "0.3.9" 3418 + source = "registry+https://github.com/rust-lang/crates.io-index" 3419 + checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 3420 + dependencies = [ 3421 + "winapi-i686-pc-windows-gnu", 3422 + "winapi-x86_64-pc-windows-gnu", 3423 + ] 3424 + 3425 + [[package]] 3426 + name = "winapi-i686-pc-windows-gnu" 3427 + version = "0.4.0" 3428 + source = "registry+https://github.com/rust-lang/crates.io-index" 3429 + checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 3430 + 3431 + [[package]] 3432 + name = "winapi-util" 3433 + version = "0.1.8" 3434 + source = "registry+https://github.com/rust-lang/crates.io-index" 3435 + checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" 3436 + dependencies = [ 3437 + "windows-sys 0.52.0", 3438 + ] 3439 + 3440 + [[package]] 3441 + name = "winapi-x86_64-pc-windows-gnu" 3442 + version = "0.4.0" 3443 + source = "registry+https://github.com/rust-lang/crates.io-index" 3444 + checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 3445 + 3446 + [[package]] 3447 + name = "windows-core" 3448 + version = "0.52.0" 3449 + source = "registry+https://github.com/rust-lang/crates.io-index" 3450 + checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" 3451 + dependencies = [ 3452 + "windows-targets 0.52.5", 3453 + ] 3454 + 3455 + [[package]] 3456 + name = "windows-sys" 3457 + version = "0.48.0" 3458 + source = "registry+https://github.com/rust-lang/crates.io-index" 3459 + checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 3460 + dependencies = [ 3461 + "windows-targets 0.48.5", 3462 + ] 3463 + 3464 + [[package]] 3465 + name = "windows-sys" 3466 + version = "0.52.0" 3467 + source = "registry+https://github.com/rust-lang/crates.io-index" 3468 + checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 3469 + dependencies = [ 3470 + "windows-targets 0.52.5", 3471 + ] 3472 + 3473 + [[package]] 3474 + name = "windows-targets" 3475 + version = "0.48.5" 3476 + source = "registry+https://github.com/rust-lang/crates.io-index" 3477 + checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 3478 + dependencies = [ 3479 + "windows_aarch64_gnullvm 0.48.5", 3480 + "windows_aarch64_msvc 0.48.5", 3481 + "windows_i686_gnu 0.48.5", 3482 + "windows_i686_msvc 0.48.5", 3483 + "windows_x86_64_gnu 0.48.5", 3484 + "windows_x86_64_gnullvm 0.48.5", 3485 + "windows_x86_64_msvc 0.48.5", 3486 + ] 3487 + 3488 + [[package]] 3489 + name = "windows-targets" 3490 + version = "0.52.5" 3491 + source = "registry+https://github.com/rust-lang/crates.io-index" 3492 + checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" 3493 + dependencies = [ 3494 + "windows_aarch64_gnullvm 0.52.5", 3495 + "windows_aarch64_msvc 0.52.5", 3496 + "windows_i686_gnu 0.52.5", 3497 + "windows_i686_gnullvm", 3498 + "windows_i686_msvc 0.52.5", 3499 + "windows_x86_64_gnu 0.52.5", 3500 + "windows_x86_64_gnullvm 0.52.5", 3501 + "windows_x86_64_msvc 0.52.5", 3502 + ] 3503 + 3504 + [[package]] 3505 + name = "windows_aarch64_gnullvm" 3506 + version = "0.48.5" 3507 + source = "registry+https://github.com/rust-lang/crates.io-index" 3508 + checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 3509 + 3510 + [[package]] 3511 + name = "windows_aarch64_gnullvm" 3512 + version = "0.52.5" 3513 + source = "registry+https://github.com/rust-lang/crates.io-index" 3514 + checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" 3515 + 3516 + [[package]] 3517 + name = "windows_aarch64_msvc" 3518 + version = "0.48.5" 3519 + source = "registry+https://github.com/rust-lang/crates.io-index" 3520 + checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 3521 + 3522 + [[package]] 3523 + name = "windows_aarch64_msvc" 3524 + version = "0.52.5" 3525 + source = "registry+https://github.com/rust-lang/crates.io-index" 3526 + checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" 3527 + 3528 + [[package]] 3529 + name = "windows_i686_gnu" 3530 + version = "0.48.5" 3531 + source = "registry+https://github.com/rust-lang/crates.io-index" 3532 + checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 3533 + 3534 + [[package]] 3535 + name = "windows_i686_gnu" 3536 + version = "0.52.5" 3537 + source = "registry+https://github.com/rust-lang/crates.io-index" 3538 + checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" 3539 + 3540 + [[package]] 3541 + name = "windows_i686_gnullvm" 3542 + version = "0.52.5" 3543 + source = "registry+https://github.com/rust-lang/crates.io-index" 3544 + checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" 3545 + 3546 + [[package]] 3547 + name = "windows_i686_msvc" 3548 + version = "0.48.5" 3549 + source = "registry+https://github.com/rust-lang/crates.io-index" 3550 + checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 3551 + 3552 + [[package]] 3553 + name = "windows_i686_msvc" 3554 + version = "0.52.5" 3555 + source = "registry+https://github.com/rust-lang/crates.io-index" 3556 + checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" 3557 + 3558 + [[package]] 3559 + name = "windows_x86_64_gnu" 3560 + version = "0.48.5" 3561 + source = "registry+https://github.com/rust-lang/crates.io-index" 3562 + checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 3563 + 3564 + [[package]] 3565 + name = "windows_x86_64_gnu" 3566 + version = "0.52.5" 3567 + source = "registry+https://github.com/rust-lang/crates.io-index" 3568 + checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" 3569 + 3570 + [[package]] 3571 + name = "windows_x86_64_gnullvm" 3572 + version = "0.48.5" 3573 + source = "registry+https://github.com/rust-lang/crates.io-index" 3574 + checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 3575 + 3576 + [[package]] 3577 + name = "windows_x86_64_gnullvm" 3578 + version = "0.52.5" 3579 + source = "registry+https://github.com/rust-lang/crates.io-index" 3580 + checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" 3581 + 3582 + [[package]] 3583 + name = "windows_x86_64_msvc" 3584 + version = "0.48.5" 3585 + source = "registry+https://github.com/rust-lang/crates.io-index" 3586 + checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 3587 + 3588 + [[package]] 3589 + name = "windows_x86_64_msvc" 3590 + version = "0.52.5" 3591 + source = "registry+https://github.com/rust-lang/crates.io-index" 3592 + checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" 3593 + 3594 + [[package]] 3595 + name = "winnow" 3596 + version = "0.6.13" 3597 + source = "registry+https://github.com/rust-lang/crates.io-index" 3598 + checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" 3599 + dependencies = [ 3600 + "memchr", 3601 + ] 3602 + 3603 + [[package]] 3604 + name = "winreg" 3605 + version = "0.52.0" 3606 + source = "registry+https://github.com/rust-lang/crates.io-index" 3607 + checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" 3608 + dependencies = [ 3609 + "cfg-if", 3610 + "windows-sys 0.48.0", 3611 + ] 3612 + 3613 + [[package]] 3614 + name = "zerocopy" 3615 + version = "0.7.34" 3616 + source = "registry+https://github.com/rust-lang/crates.io-index" 3617 + checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" 3618 + dependencies = [ 3619 + "zerocopy-derive", 3620 + ] 3621 + 3622 + [[package]] 3623 + name = "zerocopy-derive" 3624 + version = "0.7.34" 3625 + source = "registry+https://github.com/rust-lang/crates.io-index" 3626 + checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" 3627 + dependencies = [ 3628 + "proc-macro2", 3629 + "quote", 3630 + "syn 2.0.68", 3631 + ] 3632 + 3633 + [[package]] 3634 + name = "zeroize" 3635 + version = "1.8.1" 3636 + source = "registry+https://github.com/rust-lang/crates.io-index" 3637 + checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" 3638 + 3639 + [[package]] 3640 + name = "zstd" 3641 + version = "0.13.1" 3642 + source = "registry+https://github.com/rust-lang/crates.io-index" 3643 + checksum = "2d789b1514203a1120ad2429eae43a7bd32b90976a7bb8a05f7ec02fa88cc23a" 3644 + dependencies = [ 3645 + "zstd-safe", 3646 + ] 3647 + 3648 + [[package]] 3649 + name = "zstd-safe" 3650 + version = "7.1.0" 3651 + source = "registry+https://github.com/rust-lang/crates.io-index" 3652 + checksum = "1cd99b45c6bc03a018c8b8a86025678c87e55526064e38f9df301989dce7ec0a" 3653 + dependencies = [ 3654 + "zstd-sys", 3655 + ] 3656 + 3657 + [[package]] 3658 + name = "zstd-sys" 3659 + version = "2.0.11+zstd.1.5.6" 3660 + source = "registry+https://github.com/rust-lang/crates.io-index" 3661 + checksum = "75652c55c0b6f3e6f12eb786fe1bc960396bf05a1eb3bf1f3691c3610ac2e6d4" 3662 + dependencies = [ 3663 + "cc", 3664 + "pkg-config", 3665 + ]
+80
backend-rs/Cargo.toml
··· 1 + [workspace] 2 + members = ["libs/lumina-urls"] 3 + [workspace.dependencies] 4 + serde = { version = "1.0.204", features = ["derive"] } 5 + toml = "0.8.14" 6 + reqwest = { version = "0.12.5", features = ["blocking"] } 7 + serde_json = "1.0.120" 8 + regex = "1.10.5" 9 + [workspace.package] 10 + authors = ['MLC "Strawmelonjuice" Bloeiman'] 11 + license = "BSD-3-Clause" 12 + repository = "https://github.com/strawmelonjuice/lumina" 13 + edition = "2021" 14 + license-file = "../LICENSE" 15 + 16 + [package] 17 + name = "strawmelonjuice-lumina" 18 + authors.workspace = true 19 + publish = false 20 + license.workspace = true 21 + # license-file.workspace = true 22 + repository.workspace = true 23 + version = "0.1.0" 24 + edition.workspace = true 25 + [[bin]] 26 + name = "lumina-server" 27 + path = "./src/server.rs" 28 + 29 + [profile.dev] 30 + opt-level = 3 31 + debug = true 32 + strip = "none" 33 + debug-assertions = true 34 + overflow-checks = true 35 + lto = false 36 + panic = 'unwind' 37 + incremental = true 38 + codegen-units = 256 39 + 40 + [profile.release] 41 + opt-level = 3 42 + lto = true 43 + panic = 'abort' 44 + 45 + [dependencies] 46 + regex = { workspace = true } 47 + serde = { workspace = true } 48 + reqwest = { workspace = true, features = ["blocking"] } 49 + serde_json = { workspace = true } 50 + toml = { workspace = true } 51 + lumina-urls = { path = "libs/lumina-urls" } 52 + futures = "0.3.30" 53 + async-std = "1.12.0" 54 + colored = "3.0.0" 55 + log = "0.4.21" 56 + rusqlite = { version = "0.32.1", features = ["bundled"] } 57 + time = "0.3.34" 58 + password-hash = "0.5.0" 59 + tokio = { version = "1.38.0", features = [ 60 + "rt", 61 + "rt-multi-thread", 62 + "macros", 63 + "time", 64 + ] } 65 + pbkdf2 = "0.12.2" 66 + actix-session = { version = "0.10.1", features = ["cookie-session"] } 67 + actix-identity = "0.8.0" 68 + actix-web = "4.7.0" 69 + handlebars = "6.2.0" 70 + postgres = "0.19.9" 71 + home = "0.5.9" 72 + magic-crypt = "4.0.1" 73 + rand = "0.8.5" 74 + build_const = "0.2.2" 75 + markdown = "1.0.0-alpha.18" 76 + chrono = "0.4.38" 77 + simplelog = "0.12.2" 78 + dotenv = "0.15.0" 79 + indicatif = "0.17.8" 80 + console = "0.15.8"
backend-rs/frontend_assets/fonts/Fira_Sans/FiraSans-Black.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Fira_Sans/FiraSans-BlackItalic.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Fira_Sans/FiraSans-Bold.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Fira_Sans/FiraSans-BoldItalic.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Fira_Sans/FiraSans-ExtraBold.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Fira_Sans/FiraSans-ExtraBoldItalic.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Fira_Sans/FiraSans-ExtraLight.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Fira_Sans/FiraSans-ExtraLightItalic.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Fira_Sans/FiraSans-Italic.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Fira_Sans/FiraSans-Light.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Fira_Sans/FiraSans-LightItalic.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Fira_Sans/FiraSans-Medium.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Fira_Sans/FiraSans-MediumItalic.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Fira_Sans/FiraSans-Regular.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Fira_Sans/FiraSans-SemiBold.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Fira_Sans/FiraSans-SemiBoldItalic.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Fira_Sans/FiraSans-Thin.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Fira_Sans/FiraSans-ThinItalic.ttf

This is a binary file and will not be displayed.

+93
backend-rs/frontend_assets/fonts/Fira_Sans/OFL.txt
··· 1 + Copyright (c) 2012-2015, The Mozilla Foundation and Telefonica S.A. 2 + 3 + This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 + This license is copied below, and is also available with a FAQ at: 5 + https://openfontlicense.org 6 + 7 + 8 + ----------------------------------------------------------- 9 + SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 + ----------------------------------------------------------- 11 + 12 + PREAMBLE 13 + The goals of the Open Font License (OFL) are to stimulate worldwide 14 + development of collaborative font projects, to support the font creation 15 + efforts of academic and linguistic communities, and to provide a free and 16 + open framework in which fonts may be shared and improved in partnership 17 + with others. 18 + 19 + The OFL allows the licensed fonts to be used, studied, modified and 20 + redistributed freely as long as they are not sold by themselves. The 21 + fonts, including any derivative works, can be bundled, embedded, 22 + redistributed and/or sold with any software provided that any reserved 23 + names are not used by derivative works. The fonts and derivatives, 24 + however, cannot be released under any other type of license. The 25 + requirement for fonts to remain under this license does not apply 26 + to any document created using the fonts or their derivatives. 27 + 28 + DEFINITIONS 29 + "Font Software" refers to the set of files released by the Copyright 30 + Holder(s) under this license and clearly marked as such. This may 31 + include source files, build scripts and documentation. 32 + 33 + "Reserved Font Name" refers to any names specified as such after the 34 + copyright statement(s). 35 + 36 + "Original Version" refers to the collection of Font Software components as 37 + distributed by the Copyright Holder(s). 38 + 39 + "Modified Version" refers to any derivative made by adding to, deleting, 40 + or substituting -- in part or in whole -- any of the components of the 41 + Original Version, by changing formats or by porting the Font Software to a 42 + new environment. 43 + 44 + "Author" refers to any designer, engineer, programmer, technical 45 + writer or other person who contributed to the Font Software. 46 + 47 + PERMISSION & CONDITIONS 48 + Permission is hereby granted, free of charge, to any person obtaining 49 + a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 + redistribute, and sell modified and unmodified copies of the Font 51 + Software, subject to the following conditions: 52 + 53 + 1) Neither the Font Software nor any of its individual components, 54 + in Original or Modified Versions, may be sold by itself. 55 + 56 + 2) Original or Modified Versions of the Font Software may be bundled, 57 + redistributed and/or sold with any software, provided that each copy 58 + contains the above copyright notice and this license. These can be 59 + included either as stand-alone text files, human-readable headers or 60 + in the appropriate machine-readable metadata fields within text or 61 + binary files as long as those fields can be easily viewed by the user. 62 + 63 + 3) No Modified Version of the Font Software may use the Reserved Font 64 + Name(s) unless explicit written permission is granted by the corresponding 65 + Copyright Holder. This restriction only applies to the primary font name as 66 + presented to the users. 67 + 68 + 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 + Software shall not be used to promote, endorse or advertise any 70 + Modified Version, except to acknowledge the contribution(s) of the 71 + Copyright Holder(s) and the Author(s) or with their explicit written 72 + permission. 73 + 74 + 5) The Font Software, modified or unmodified, in part or in whole, 75 + must be distributed entirely under this license, and must not be 76 + distributed under any other license. The requirement for fonts to 77 + remain under this license does not apply to any document created 78 + using the Font Software. 79 + 80 + TERMINATION 81 + This license becomes null and void if any of the above conditions are 82 + not met. 83 + 84 + DISCLAIMER 85 + THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 + OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 + COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 + INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 + DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 + FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 + OTHER DEALINGS IN THE FONT SOFTWARE.
backend-rs/frontend_assets/fonts/Gantari/Gantari-Italic-VariableFont_wght.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Gantari/Gantari-VariableFont_wght.ttf

This is a binary file and will not be displayed.

+93
backend-rs/frontend_assets/fonts/Gantari/OFL.txt
··· 1 + Copyright 2022 The Gantari Project Authors (https://github.com/Lafontype/Gantari) 2 + 3 + This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 + This license is copied below, and is also available with a FAQ at: 5 + https://openfontlicense.org 6 + 7 + 8 + ----------------------------------------------------------- 9 + SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 + ----------------------------------------------------------- 11 + 12 + PREAMBLE 13 + The goals of the Open Font License (OFL) are to stimulate worldwide 14 + development of collaborative font projects, to support the font creation 15 + efforts of academic and linguistic communities, and to provide a free and 16 + open framework in which fonts may be shared and improved in partnership 17 + with others. 18 + 19 + The OFL allows the licensed fonts to be used, studied, modified and 20 + redistributed freely as long as they are not sold by themselves. The 21 + fonts, including any derivative works, can be bundled, embedded, 22 + redistributed and/or sold with any software provided that any reserved 23 + names are not used by derivative works. The fonts and derivatives, 24 + however, cannot be released under any other type of license. The 25 + requirement for fonts to remain under this license does not apply 26 + to any document created using the fonts or their derivatives. 27 + 28 + DEFINITIONS 29 + "Font Software" refers to the set of files released by the Copyright 30 + Holder(s) under this license and clearly marked as such. This may 31 + include source files, build scripts and documentation. 32 + 33 + "Reserved Font Name" refers to any names specified as such after the 34 + copyright statement(s). 35 + 36 + "Original Version" refers to the collection of Font Software components as 37 + distributed by the Copyright Holder(s). 38 + 39 + "Modified Version" refers to any derivative made by adding to, deleting, 40 + or substituting -- in part or in whole -- any of the components of the 41 + Original Version, by changing formats or by porting the Font Software to a 42 + new environment. 43 + 44 + "Author" refers to any designer, engineer, programmer, technical 45 + writer or other person who contributed to the Font Software. 46 + 47 + PERMISSION & CONDITIONS 48 + Permission is hereby granted, free of charge, to any person obtaining 49 + a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 + redistribute, and sell modified and unmodified copies of the Font 51 + Software, subject to the following conditions: 52 + 53 + 1) Neither the Font Software nor any of its individual components, 54 + in Original or Modified Versions, may be sold by itself. 55 + 56 + 2) Original or Modified Versions of the Font Software may be bundled, 57 + redistributed and/or sold with any software, provided that each copy 58 + contains the above copyright notice and this license. These can be 59 + included either as stand-alone text files, human-readable headers or 60 + in the appropriate machine-readable metadata fields within text or 61 + binary files as long as those fields can be easily viewed by the user. 62 + 63 + 3) No Modified Version of the Font Software may use the Reserved Font 64 + Name(s) unless explicit written permission is granted by the corresponding 65 + Copyright Holder. This restriction only applies to the primary font name as 66 + presented to the users. 67 + 68 + 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 + Software shall not be used to promote, endorse or advertise any 70 + Modified Version, except to acknowledge the contribution(s) of the 71 + Copyright Holder(s) and the Author(s) or with their explicit written 72 + permission. 73 + 74 + 5) The Font Software, modified or unmodified, in part or in whole, 75 + must be distributed entirely under this license, and must not be 76 + distributed under any other license. The requirement for fonts to 77 + remain under this license does not apply to any document created 78 + using the Font Software. 79 + 80 + TERMINATION 81 + This license becomes null and void if any of the above conditions are 82 + not met. 83 + 84 + DISCLAIMER 85 + THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 + OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 + COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 + INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 + DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 + FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 + OTHER DEALINGS IN THE FONT SOFTWARE.
+81
backend-rs/frontend_assets/fonts/Gantari/README.txt
··· 1 + Gantari Variable Font 2 + ===================== 3 + 4 + This download contains Gantari as both variable fonts and static fonts. 5 + 6 + Gantari is a variable font with this axis: 7 + wght 8 + 9 + This means all the styles are contained in these files: 10 + Gantari/Gantari-VariableFont_wght.ttf 11 + Gantari/Gantari-Italic-VariableFont_wght.ttf 12 + 13 + If your app fully supports variable fonts, you can now pick intermediate styles 14 + that aren’t available as static fonts. Not all apps support variable fonts, and 15 + in those cases you can use the static font files for Gantari: 16 + Gantari/static/Gantari-Thin.ttf 17 + Gantari/static/Gantari-ExtraLight.ttf 18 + Gantari/static/Gantari-Light.ttf 19 + Gantari/static/Gantari-Regular.ttf 20 + Gantari/static/Gantari-Medium.ttf 21 + Gantari/static/Gantari-SemiBold.ttf 22 + Gantari/static/Gantari-Bold.ttf 23 + Gantari/static/Gantari-ExtraBold.ttf 24 + Gantari/static/Gantari-Black.ttf 25 + Gantari/static/Gantari-ThinItalic.ttf 26 + Gantari/static/Gantari-ExtraLightItalic.ttf 27 + Gantari/static/Gantari-LightItalic.ttf 28 + Gantari/static/Gantari-Italic.ttf 29 + Gantari/static/Gantari-MediumItalic.ttf 30 + Gantari/static/Gantari-SemiBoldItalic.ttf 31 + Gantari/static/Gantari-BoldItalic.ttf 32 + Gantari/static/Gantari-ExtraBoldItalic.ttf 33 + Gantari/static/Gantari-BlackItalic.ttf 34 + 35 + Get started 36 + ----------- 37 + 38 + 1. Install the font files you want to use 39 + 40 + 2. Use your app's font picker to view the font family and all the 41 + available styles 42 + 43 + Learn more about variable fonts 44 + ------------------------------- 45 + 46 + https://developers.google.com/web/fundamentals/design-and-ux/typography/variable-fonts 47 + https://variablefonts.typenetwork.com 48 + https://medium.com/variable-fonts 49 + 50 + In desktop apps 51 + 52 + https://theblog.adobe.com/can-variable-fonts-illustrator-cc 53 + https://helpx.adobe.com/nz/photoshop/using/fonts.html#variable_fonts 54 + 55 + Online 56 + 57 + https://developers.google.com/fonts/docs/getting_started 58 + https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide 59 + https://developer.microsoft.com/en-us/microsoft-edge/testdrive/demos/variable-fonts 60 + 61 + Installing fonts 62 + 63 + MacOS: https://support.apple.com/en-us/HT201749 64 + Linux: https://www.google.com/search?q=how+to+install+a+font+on+gnu%2Blinux 65 + Windows: https://support.microsoft.com/en-us/help/314960/how-to-install-or-remove-a-font-in-windows 66 + 67 + Android Apps 68 + 69 + https://developers.google.com/fonts/docs/android 70 + https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts 71 + 72 + License 73 + ------- 74 + Please read the full license text (OFL.txt) to understand the permissions, 75 + restrictions and requirements for usage, redistribution, and modification. 76 + 77 + You can use them in your products & projects – print or digital, 78 + commercial or otherwise. 79 + 80 + This isn't legal advice, please consider consulting a lawyer and see the full 81 + license for all details.
backend-rs/frontend_assets/fonts/Gantari/static/Gantari-Black.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Gantari/static/Gantari-BlackItalic.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Gantari/static/Gantari-Bold.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Gantari/static/Gantari-BoldItalic.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Gantari/static/Gantari-ExtraBold.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Gantari/static/Gantari-ExtraBoldItalic.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Gantari/static/Gantari-ExtraLight.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Gantari/static/Gantari-ExtraLightItalic.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Gantari/static/Gantari-Italic.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Gantari/static/Gantari-Light.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Gantari/static/Gantari-LightItalic.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Gantari/static/Gantari-Medium.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Gantari/static/Gantari-MediumItalic.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Gantari/static/Gantari-Regular.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Gantari/static/Gantari-SemiBold.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Gantari/static/Gantari-SemiBoldItalic.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Gantari/static/Gantari-Thin.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Gantari/static/Gantari-ThinItalic.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Josefin_Sans/JosefinSans-Italic-VariableFont_wght.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Josefin_Sans/JosefinSans-VariableFont_wght.ttf

This is a binary file and will not be displayed.

+93
backend-rs/frontend_assets/fonts/Josefin_Sans/OFL.txt
··· 1 + Copyright 2010 The Josefin Sans Project Authors (https://github.com/ThomasJockin/JosefinSansFont-master), with Reserved Font Name "Josefin Sans". 2 + 3 + This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 + This license is copied below, and is also available with a FAQ at: 5 + https://openfontlicense.org 6 + 7 + 8 + ----------------------------------------------------------- 9 + SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 + ----------------------------------------------------------- 11 + 12 + PREAMBLE 13 + The goals of the Open Font License (OFL) are to stimulate worldwide 14 + development of collaborative font projects, to support the font creation 15 + efforts of academic and linguistic communities, and to provide a free and 16 + open framework in which fonts may be shared and improved in partnership 17 + with others. 18 + 19 + The OFL allows the licensed fonts to be used, studied, modified and 20 + redistributed freely as long as they are not sold by themselves. The 21 + fonts, including any derivative works, can be bundled, embedded, 22 + redistributed and/or sold with any software provided that any reserved 23 + names are not used by derivative works. The fonts and derivatives, 24 + however, cannot be released under any other type of license. The 25 + requirement for fonts to remain under this license does not apply 26 + to any document created using the fonts or their derivatives. 27 + 28 + DEFINITIONS 29 + "Font Software" refers to the set of files released by the Copyright 30 + Holder(s) under this license and clearly marked as such. This may 31 + include source files, build scripts and documentation. 32 + 33 + "Reserved Font Name" refers to any names specified as such after the 34 + copyright statement(s). 35 + 36 + "Original Version" refers to the collection of Font Software components as 37 + distributed by the Copyright Holder(s). 38 + 39 + "Modified Version" refers to any derivative made by adding to, deleting, 40 + or substituting -- in part or in whole -- any of the components of the 41 + Original Version, by changing formats or by porting the Font Software to a 42 + new environment. 43 + 44 + "Author" refers to any designer, engineer, programmer, technical 45 + writer or other person who contributed to the Font Software. 46 + 47 + PERMISSION & CONDITIONS 48 + Permission is hereby granted, free of charge, to any person obtaining 49 + a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 + redistribute, and sell modified and unmodified copies of the Font 51 + Software, subject to the following conditions: 52 + 53 + 1) Neither the Font Software nor any of its individual components, 54 + in Original or Modified Versions, may be sold by itself. 55 + 56 + 2) Original or Modified Versions of the Font Software may be bundled, 57 + redistributed and/or sold with any software, provided that each copy 58 + contains the above copyright notice and this license. These can be 59 + included either as stand-alone text files, human-readable headers or 60 + in the appropriate machine-readable metadata fields within text or 61 + binary files as long as those fields can be easily viewed by the user. 62 + 63 + 3) No Modified Version of the Font Software may use the Reserved Font 64 + Name(s) unless explicit written permission is granted by the corresponding 65 + Copyright Holder. This restriction only applies to the primary font name as 66 + presented to the users. 67 + 68 + 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 + Software shall not be used to promote, endorse or advertise any 70 + Modified Version, except to acknowledge the contribution(s) of the 71 + Copyright Holder(s) and the Author(s) or with their explicit written 72 + permission. 73 + 74 + 5) The Font Software, modified or unmodified, in part or in whole, 75 + must be distributed entirely under this license, and must not be 76 + distributed under any other license. The requirement for fonts to 77 + remain under this license does not apply to any document created 78 + using the Font Software. 79 + 80 + TERMINATION 81 + This license becomes null and void if any of the above conditions are 82 + not met. 83 + 84 + DISCLAIMER 85 + THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 + OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 + COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 + INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 + DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 + FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 + OTHER DEALINGS IN THE FONT SOFTWARE.
+77
backend-rs/frontend_assets/fonts/Josefin_Sans/README.txt
··· 1 + Josefin Sans Variable Font 2 + ========================== 3 + 4 + This download contains Josefin Sans as both variable fonts and static fonts. 5 + 6 + Josefin Sans is a variable font with this axis: 7 + wght 8 + 9 + This means all the styles are contained in these files: 10 + Josefin_Sans/JosefinSans-VariableFont_wght.ttf 11 + Josefin_Sans/JosefinSans-Italic-VariableFont_wght.ttf 12 + 13 + If your app fully supports variable fonts, you can now pick intermediate styles 14 + that aren’t available as static fonts. Not all apps support variable fonts, and 15 + in those cases you can use the static font files for Josefin Sans: 16 + Josefin_Sans/static/JosefinSans-Thin.ttf 17 + Josefin_Sans/static/JosefinSans-ExtraLight.ttf 18 + Josefin_Sans/static/JosefinSans-Light.ttf 19 + Josefin_Sans/static/JosefinSans-Regular.ttf 20 + Josefin_Sans/static/JosefinSans-Medium.ttf 21 + Josefin_Sans/static/JosefinSans-SemiBold.ttf 22 + Josefin_Sans/static/JosefinSans-Bold.ttf 23 + Josefin_Sans/static/JosefinSans-ThinItalic.ttf 24 + Josefin_Sans/static/JosefinSans-ExtraLightItalic.ttf 25 + Josefin_Sans/static/JosefinSans-LightItalic.ttf 26 + Josefin_Sans/static/JosefinSans-Italic.ttf 27 + Josefin_Sans/static/JosefinSans-MediumItalic.ttf 28 + Josefin_Sans/static/JosefinSans-SemiBoldItalic.ttf 29 + Josefin_Sans/static/JosefinSans-BoldItalic.ttf 30 + 31 + Get started 32 + ----------- 33 + 34 + 1. Install the font files you want to use 35 + 36 + 2. Use your app's font picker to view the font family and all the 37 + available styles 38 + 39 + Learn more about variable fonts 40 + ------------------------------- 41 + 42 + https://developers.google.com/web/fundamentals/design-and-ux/typography/variable-fonts 43 + https://variablefonts.typenetwork.com 44 + https://medium.com/variable-fonts 45 + 46 + In desktop apps 47 + 48 + https://theblog.adobe.com/can-variable-fonts-illustrator-cc 49 + https://helpx.adobe.com/nz/photoshop/using/fonts.html#variable_fonts 50 + 51 + Online 52 + 53 + https://developers.google.com/fonts/docs/getting_started 54 + https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide 55 + https://developer.microsoft.com/en-us/microsoft-edge/testdrive/demos/variable-fonts 56 + 57 + Installing fonts 58 + 59 + MacOS: https://support.apple.com/en-us/HT201749 60 + Linux: https://www.google.com/search?q=how+to+install+a+font+on+gnu%2Blinux 61 + Windows: https://support.microsoft.com/en-us/help/314960/how-to-install-or-remove-a-font-in-windows 62 + 63 + Android Apps 64 + 65 + https://developers.google.com/fonts/docs/android 66 + https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts 67 + 68 + License 69 + ------- 70 + Please read the full license text (OFL.txt) to understand the permissions, 71 + restrictions and requirements for usage, redistribution, and modification. 72 + 73 + You can use them in your products & projects – print or digital, 74 + commercial or otherwise. 75 + 76 + This isn't legal advice, please consider consulting a lawyer and see the full 77 + license for all details.
backend-rs/frontend_assets/fonts/Josefin_Sans/static/JosefinSans-Bold.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Josefin_Sans/static/JosefinSans-BoldItalic.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Josefin_Sans/static/JosefinSans-ExtraLight.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Josefin_Sans/static/JosefinSans-ExtraLightItalic.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Josefin_Sans/static/JosefinSans-Italic.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Josefin_Sans/static/JosefinSans-Light.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Josefin_Sans/static/JosefinSans-LightItalic.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Josefin_Sans/static/JosefinSans-Medium.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Josefin_Sans/static/JosefinSans-MediumItalic.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Josefin_Sans/static/JosefinSans-Regular.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Josefin_Sans/static/JosefinSans-SemiBold.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Josefin_Sans/static/JosefinSans-SemiBoldItalic.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Josefin_Sans/static/JosefinSans-Thin.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Josefin_Sans/static/JosefinSans-ThinItalic.ttf

This is a binary file and will not be displayed.

+93
backend-rs/frontend_assets/fonts/Syne/OFL.txt
··· 1 + Copyright 2017 The Syne Project Authors (https://gitlab.com/bonjour-monde/fonderie/syne-typeface) 2 + 3 + This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 + This license is copied below, and is also available with a FAQ at: 5 + https://openfontlicense.org 6 + 7 + 8 + ----------------------------------------------------------- 9 + SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 + ----------------------------------------------------------- 11 + 12 + PREAMBLE 13 + The goals of the Open Font License (OFL) are to stimulate worldwide 14 + development of collaborative font projects, to support the font creation 15 + efforts of academic and linguistic communities, and to provide a free and 16 + open framework in which fonts may be shared and improved in partnership 17 + with others. 18 + 19 + The OFL allows the licensed fonts to be used, studied, modified and 20 + redistributed freely as long as they are not sold by themselves. The 21 + fonts, including any derivative works, can be bundled, embedded, 22 + redistributed and/or sold with any software provided that any reserved 23 + names are not used by derivative works. The fonts and derivatives, 24 + however, cannot be released under any other type of license. The 25 + requirement for fonts to remain under this license does not apply 26 + to any document created using the fonts or their derivatives. 27 + 28 + DEFINITIONS 29 + "Font Software" refers to the set of files released by the Copyright 30 + Holder(s) under this license and clearly marked as such. This may 31 + include source files, build scripts and documentation. 32 + 33 + "Reserved Font Name" refers to any names specified as such after the 34 + copyright statement(s). 35 + 36 + "Original Version" refers to the collection of Font Software components as 37 + distributed by the Copyright Holder(s). 38 + 39 + "Modified Version" refers to any derivative made by adding to, deleting, 40 + or substituting -- in part or in whole -- any of the components of the 41 + Original Version, by changing formats or by porting the Font Software to a 42 + new environment. 43 + 44 + "Author" refers to any designer, engineer, programmer, technical 45 + writer or other person who contributed to the Font Software. 46 + 47 + PERMISSION & CONDITIONS 48 + Permission is hereby granted, free of charge, to any person obtaining 49 + a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 + redistribute, and sell modified and unmodified copies of the Font 51 + Software, subject to the following conditions: 52 + 53 + 1) Neither the Font Software nor any of its individual components, 54 + in Original or Modified Versions, may be sold by itself. 55 + 56 + 2) Original or Modified Versions of the Font Software may be bundled, 57 + redistributed and/or sold with any software, provided that each copy 58 + contains the above copyright notice and this license. These can be 59 + included either as stand-alone text files, human-readable headers or 60 + in the appropriate machine-readable metadata fields within text or 61 + binary files as long as those fields can be easily viewed by the user. 62 + 63 + 3) No Modified Version of the Font Software may use the Reserved Font 64 + Name(s) unless explicit written permission is granted by the corresponding 65 + Copyright Holder. This restriction only applies to the primary font name as 66 + presented to the users. 67 + 68 + 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 + Software shall not be used to promote, endorse or advertise any 70 + Modified Version, except to acknowledge the contribution(s) of the 71 + Copyright Holder(s) and the Author(s) or with their explicit written 72 + permission. 73 + 74 + 5) The Font Software, modified or unmodified, in part or in whole, 75 + must be distributed entirely under this license, and must not be 76 + distributed under any other license. The requirement for fonts to 77 + remain under this license does not apply to any document created 78 + using the Font Software. 79 + 80 + TERMINATION 81 + This license becomes null and void if any of the above conditions are 82 + not met. 83 + 84 + DISCLAIMER 85 + THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 + OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 + COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 + INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 + DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 + FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 + OTHER DEALINGS IN THE FONT SOFTWARE.
+67
backend-rs/frontend_assets/fonts/Syne/README.txt
··· 1 + Syne Variable Font 2 + ================== 3 + 4 + This download contains Syne as both a variable font and static fonts. 5 + 6 + Syne is a variable font with this axis: 7 + wght 8 + 9 + This means all the styles are contained in a single file: 10 + Syne/Syne-VariableFont_wght.ttf 11 + 12 + If your app fully supports variable fonts, you can now pick intermediate styles 13 + that aren’t available as static fonts. Not all apps support variable fonts, and 14 + in those cases you can use the static font files for Syne: 15 + Syne/static/Syne-Regular.ttf 16 + Syne/static/Syne-Medium.ttf 17 + Syne/static/Syne-SemiBold.ttf 18 + Syne/static/Syne-Bold.ttf 19 + Syne/static/Syne-ExtraBold.ttf 20 + 21 + Get started 22 + ----------- 23 + 24 + 1. Install the font files you want to use 25 + 26 + 2. Use your app's font picker to view the font family and all the 27 + available styles 28 + 29 + Learn more about variable fonts 30 + ------------------------------- 31 + 32 + https://developers.google.com/web/fundamentals/design-and-ux/typography/variable-fonts 33 + https://variablefonts.typenetwork.com 34 + https://medium.com/variable-fonts 35 + 36 + In desktop apps 37 + 38 + https://theblog.adobe.com/can-variable-fonts-illustrator-cc 39 + https://helpx.adobe.com/nz/photoshop/using/fonts.html#variable_fonts 40 + 41 + Online 42 + 43 + https://developers.google.com/fonts/docs/getting_started 44 + https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide 45 + https://developer.microsoft.com/en-us/microsoft-edge/testdrive/demos/variable-fonts 46 + 47 + Installing fonts 48 + 49 + MacOS: https://support.apple.com/en-us/HT201749 50 + Linux: https://www.google.com/search?q=how+to+install+a+font+on+gnu%2Blinux 51 + Windows: https://support.microsoft.com/en-us/help/314960/how-to-install-or-remove-a-font-in-windows 52 + 53 + Android Apps 54 + 55 + https://developers.google.com/fonts/docs/android 56 + https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts 57 + 58 + License 59 + ------- 60 + Please read the full license text (OFL.txt) to understand the permissions, 61 + restrictions and requirements for usage, redistribution, and modification. 62 + 63 + You can use them in your products & projects – print or digital, 64 + commercial or otherwise. 65 + 66 + This isn't legal advice, please consider consulting a lawyer and see the full 67 + license for all details.
backend-rs/frontend_assets/fonts/Syne/Syne-VariableFont_wght.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Syne/static/Syne-Bold.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Syne/static/Syne-ExtraBold.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Syne/static/Syne-Medium.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Syne/static/Syne-Regular.ttf

This is a binary file and will not be displayed.

backend-rs/frontend_assets/fonts/Syne/static/Syne-SemiBold.ttf

This is a binary file and will not be displayed.

+71
backend-rs/frontend_assets/handlebars/postrender.handlebars
··· 1 + <div class="container py-8 px-4 mx-auto mr-1 ml-1 lg:mr-auto lg:ml-auto lg:max-w-[50VW]"> 2 + <div class="overflow-hidden relative rounded-lg shadow-lg bg-brown-25 dark:bg-neutral-700"> 3 + {{#if (numequal posttype 2)}} 4 + {{!-- Display title for article posts --}} 5 + <h3 class="p-4 border-b-2 border-dotted border-neutral-700 dark:border-brown-25">{title}</h3> 6 + {{/if}} 7 + {{#if (numequal posttype 3)}} 8 + {{!-- Display media in media posts --}} 9 + <div class="postcontent"> 10 + <embed loading="lazy" src="https://via.placeholder.com/400x800" alt="Social Media Post" class="object-contain w-full rounded-b-none rounded-t-lg lg:max-h-[60VH]" /> 11 + <div class="p-4"> 12 + <p class="text-lg text-neutral-900 dark:text-neutral-100">{{content}}</p> 13 + </div> 14 + </div> 15 + {{/if}} 16 + <div class="relative w-full border-b-2 border-dotted border-neutral-700 dark:border-brown-25"> 17 + <div class="float-right p-2 w-max text-indigo-300 unparsed-timestamp dark:text-neutral-400">{{timestamp}}</div> 18 + <a href="{{#unless local}}https://{{author.instance}}{{/unless}}/user/profile/{{author.id}}"> 19 + <div class="flex flex-row justify-evenly p-2 w-max min-w-60 post-author-main"> 20 + <div class="w-1/5 text-xl"> 21 + <img loading="lazy" class="w-8 h-8 rounded-full" src="{{#unless local}}https://{{author.instance}}{{/unless}}/user/avatar/{{author.id}}" alt="@"> 22 + </div> 23 + <span class="text-lg">{{author.username}}{{#unless local}}@{{author.instance}}{{/unless}}</span> 24 + </div> 25 + </a> 26 + </div> 27 + {{#if (numequal posttype 1)}} 28 + <div class="border-b-2 border-dotted postcontent border-neutral-700 dark:border-brown-25"> 29 + <div class="p-4 bg-brown-100 dark:bg-neutral-800"> 30 + <p class="text-lg text-neutral-900 dark:text-neutral-100">{{content}}</p> 31 + </div> 32 + </div> 33 + {{/if}} 34 + <div class="relative mr-1 ml-1 w-full"> 35 + <div class="mt-4 mb-4 w-full"> 36 + {{#if tags}} 37 + <p class="text-xs">Tags on this post</p> 38 + <ul class="justify-between p-4 pb-1 w-full"> 39 + {{#each tags}} 40 + {{!-- Displaying tags -- if any -- in their boxy little list. --}} 41 + <li class="inline-block p-1 m-1 text-sm rounded-xl border-2 border-dotted border-neutral-400 bg-slate-600 text-neutral-200 dark:border-slate-500 dark:text-slate-800 dark:bg-slate-400"><a href="/tag/{{this}}" class="text-blue-300 dark:text-blue-900">{{this}}</a></li> 42 + {{/each}} 43 + </ul> 44 + {{/if}} 45 + </div> 46 + <div class="flex float-right justify-between items-center m-4 mr-6 w-max min-w-[9rem] h-fit min-h-12"> 47 + <div class="relative p-1 w-12 h-14 me-1 ms-1 postbttn"> 48 + <!-- Like (Push) --> 49 + <div class="absolute m-2 w-12 h-12 text-center align-middle rounded-full border-2 border-black border-solid hover:text-violet-500 bg-slate-600 text-neutral-200 leading-12 dark:text-slate-800 dark:bg-slate-400"> 50 + {{{button_push}}} 51 + </div> 52 + <span class="text-white bg-black dark:text-black postbttndescription dark:bg-brown-300">Push up</span> 53 + </div> 54 + <div class="relative p-1 w-12 h-14 me-1 ms-1 postbttn"> 55 + <!-- Reply (Comment) --> 56 + <div class="absolute m-2 w-12 h-12 text-center align-middle rounded-full border-2 border-black border-solid hover:text-red-300 bg-slate-600 text-neutral-200 leading-12 dark:text-slate-800 dark:bg-slate-400"> 57 + {{{button_comment}}} 58 + </div> 59 + <span class="text-white bg-black dark:text-black postbttndescription dark:bg-brown-300">Comment</span> 60 + </div> 61 + <div class="relative p-1 w-12 h-14 me-1 ms-1 postbttn"> 62 + <!-- Repost (Bump) --> 63 + <div class="absolute m-2 w-12 h-12 text-center align-middle rounded-full border-2 border-black border-solid hover:text-amber-400 bg-slate-600 text-neutral-200 leading-12 dark:text-slate-800 dark:bg-slate-400"> 64 + {{{button_boost}}} 65 + </div> 66 + <span class="text-white bg-black dark:text-black postbttndescription dark:bg-brown-300">Bump</span> 67 + </div> 68 + </div> 69 + </div> 70 + </div> 71 + </div>
+595
backend-rs/frontend_assets/html/examplepost.html
··· 1 + <h1>Example text post (manually edited static html)</h1> 2 + <div 3 + class="container py-8 px-4 mx-auto mr-1 ml-1 lg:mr-auto lg:ml-auto lg:max-w-[50VW]" 4 + > 5 + <div 6 + class="overflow-hidden rounded-lg shadow-lg bg-brown-25 dark:bg-neutral-700" 7 + > 8 + <!-- --> 9 + <div 10 + class="flex flex-row justify-evenly p-2 w-full border-b-2 border-dotted post-author-main border-neutral-700 h-fit dark:border-brown-25" 11 + > 12 + <div class="w-1/5"> 13 + <img 14 + class="w-8 h-8 rounded-full" 15 + src="/user/avatar/2" 16 + alt="Profilepicture" 17 + /> 18 + </div> 19 + <span>username@peonies.net</span> 20 + </div> 21 + <div 22 + class="border-b-2 border-dotted postcontent border-neutral-700 dark:border-brown-25" 23 + > 24 + <div class="p-4"> 25 + <p class="text-lg text-neutral-900 dark:text-neutral-100"> 26 + Guys... Do you think Dave Mustaine would like Gerard Way? I 27 + don't know... 28 + </p> 29 + </div> 30 + </div> 31 + <!-- --> 32 + <div class="relative w-[95%] ml-1 mr-1"> 33 + <div class="mt-4 mb-4"> 34 + <ul class="justify-between p-4 pb-1 lg:w-3/5"> 35 + <li 36 + class="inline-block p-4 mr-2 rounded-xl bg-slate-800 dark:bg-slate-400" 37 + > 38 + <a href="#" class="text-blue-300 dark:text-blue-900" 39 + >Derard Wustaine</a 40 + > 41 + </li> 42 + <li 43 + class="inline-block p-4 mr-2 rounded-xl bg-slate-800 dark:bg-slate-400" 44 + > 45 + <a href="#" class="text-blue-300 dark:text-blue-900" 46 + >MCR</a 47 + > 48 + </li> 49 + <li 50 + class="inline-block p-4 mr-2 rounded-xl bg-slate-800 dark:bg-slate-400" 51 + > 52 + <a href="#" class="text-blue-300 dark:text-blue-900" 53 + >Megadeth</a 54 + > 55 + </li> 56 + </ul> 57 + </div> 58 + <div 59 + class="flex float-right justify-between items-center m-4 w-max min-w-[9rem] h-fit min-h-12" 60 + > 61 + <div class="relative p-1 w-12 h-full me-1 ms-1"> 62 + <!-- Like (Push) --> 63 + <div 64 + class="absolute bottom-1 m-2 w-12 h-12 text-center text-white align-middle bg-black rounded-full hover:text-red-300 leading-12" 65 + > 66 + <svg 67 + width="100%" 68 + height="100%" 69 + viewBox="0 0 120 120" 70 + xmlns="http://www.w3.org/2000/svg" 71 + > 72 + <defs> 73 + <clipPath id="clipPath5"> 74 + <path 75 + d="m19.068 91.691-19.117 40.238 17.967 5.7393c0.03749-2.3926 0.1285-4.7939 0.1759-7.1882 0.02822-1.0804 0.05232-2.0481 0.09921-3.0946-8e-3 -5e-3 -0.01712-6e-3 -0.02468-0.0124-0.01979-0.0153-0.03518-0.0348-0.05276-0.0519-0.26915 0.14618-0.51212 0.26581-0.48745 0.14266 0.05519-0.27551 0.10598-0.57909 0.20412-0.85844l-8.85e-4 -3e-3c-0.63284-0.0144-1.2426-0.026-1.4864-0.036-0.26665-0.0109-0.53105-0.0305-0.79613-0.0481-0.04006 9e-3 -0.07621 0.0134-0.08863-6e-3 -0.02239-2e-3 -0.04494-3e-3 -0.06737-4e-3 0.05069-0.46211-0.03132-0.93415 0.1532-1.3859 0.0579-0.14164 0.2887-0.0371 0.43336-0.0502 0.61899-0.0562 1.2367-0.0964 1.8544-0.13825-0.0023-0.0699-0.0043-0.13959-0.0025-0.2092-0.09792 0.0126-0.19607 0.0277-0.29399 0.0399-0.46246 0.0576-1.0092 0.34357-1.387 0.14931-0.12433-0.0639-0.22304-0.15796-0.30884-0.26759l-0.0018-5.7e-4c-0.03928-0.0299-0.07122-0.0703-0.08666-0.12204-0.0011-3e-3 -0.0014-5e-3 -0.0026-8e-3 -0.14853-0.24491-0.23819-0.54641-0.30534-0.82811-0.02871-0.12038 0.24211-0.14354 0.36306-0.21532 0.70393-0.23847 1.4081-0.4755 2.1092-0.73038-0.596 0.0622-1.1922 0.12994-1.7894 0.20727-0.24049 0.0311-0.57954 0.26308-0.7239 0.10005-0.0027-3e-3 -0.0052-6e-3 -0.0078-9e-3 -0.12613 0.0206-0.25238 0.0443-0.37848 0.0634 0.01769-0.0555 0.0285-0.11385 0.04241-0.17074-0.68152 0.0559-1.3629 0.10863-2.0397 0.12951-0.1702 5e-3 -0.44996 0.14792-0.5065-2e-3 -0.55479-1.4675-0.05742-1.3382 0.98084-1.7643 0.627-0.21308 1.2562-0.41928 1.8847-0.62221-0.04459-0.0736-0.07861-0.15246-0.0944-0.24296-0.04145-0.23753 0.29729-0.47038 0.50465-0.65056 0.12746-0.11076 0.28722-0.16333 0.44208-0.22298-0.64666-0.0689-1.2785-0.14472-2.0704-0.28179-0.44702-0.0774-0.89832-0.14889-1.3244-0.27826-0.32338-0.0988-0.82844-0.0748-0.91431-0.40393-0.10587-0.40582 0.19247-0.92067 0.4648-1.3171 0.0998-0.14527 0.32726-0.0473 0.49033-0.0715 0.24416-0.0176 0.48816-0.0357 0.73233-0.0535 0.01049-0.26171 0.31701-0.30864 0.65389-0.29573 0.02096-0.0195 0.0337-0.0324 0.05549-0.0525 0.25322-0.23334 0.62915-0.0522 0.94364-0.0737 0.44116-0.0302 0.88079-0.053 1.3216-0.0803 0.48527-0.0295 0.96963-0.0542 1.4547-0.0816-1.8889-0.1577-3.7898-0.26764-5.7164-0.26169-1.334 4e-3 -1.062-1.366 0.27192-1.3699 3.6679-0.0107 7.2493 0.37575 10.819 0.76403 2.6717 0.33278 5.3402 0.67109 8.0168 0.98555l8.6816-18.273zm11.729 26.331c-1.9697-0.23241-3.9351-0.48043-5.901-0.72653 0.04753 0.38051-0.36531 0.84827-1.1411 0.87948-0.62713 0.0252-1.2524 0.0424-1.8785 0.0603-0.0209 0.0479-0.03809 0.0929-0.06253 0.14457-0.01435 0.0302-0.0341 0.0579-0.05402 0.0837 0.06731 0.0534 0.12401 0.10525 0.17416 0.15656 1.9487 0.11998 3.902 0.22074 5.8533 0.32954 0.32461 0.0195 0.65152 0.0322 0.97432 0.0589 0.56839 0.047 1.2497 0.1053 1.3204 0.5183zm-1.153 2.4268c-0.06617 0.0787-0.13902 0.16135-0.22234 0.24929-0.26738 0.28225-0.70683 0.23627-1.0631 0.32955-0.29502 0.0772-0.59127 0.14118-0.8872 0.20366 0.04695 0.20301 0.07747 0.40844 0.05087 0.62616-2.72e-4 2e-3 -0.0024 4e-3 -0.0028 6e-3 0.13341-0.0207 0.36698-0.14297 0.3763-0.0202 0.02545 0.33519-0.08951 0.68828-0.18985 1.0385l-9.29e-4 2e-3c0.01324 0.10724 0.01859 0.21712 0.01287 0.32882-0.0023 0.046-0.04668 0.0465-0.10325 0.0372-1.59e-4 1e-3 -0.0017 3e-3 -0.0019 4e-3 -0.89428 0.13879-1.7876 0.24923-2.6795 0.34854-0.70272 0.11603-1.4057 0.2375-2.1083 0.3555-0.19363 0.0321-0.38737 0.0631-0.58104 0.0952 0.10097 0.0177 0.20211 0.0347 0.3029 0.0528 0.26518 0.0365 0.52937 0.0751 0.79465 0.11122 0.14561 0.0386 0.42361-0.0366 0.43709 0.11771 1.39e-4 5e-3 -4.47e-4 9e-3 -7.8e-5 0.0131 0.39337 0.0879 0.96097 0.2208 1.2765 0.47355 3e-3 6.9e-4 0.0058 2e-3 0.0089 3e-3 0.20267 0.0508 1.4986 0.31264 1.545 0.59002 0.23172 1.3854-0.946 1.309-1.6518 1.3241-0.35844 8e-3 -0.71612 3e-3 -1.0734 4e-3 -0.37537-9e-3 -0.75081-0.0176-1.1263-0.0263-0.09645 0.0145-0.19319 0.0296-0.28959 0.0435 0.05305 0.0421 0.0253 0.15341-0.0079 0.26954 0.07786 0.14301-0.06732 0.43309-0.07158 0.63093-0.0089 0.41384-0.01706 0.82843-0.02589 1.2423-0.05917 1.7592-0.11603 3.5186-0.17921 5.2784 0.08198 0.47977 0.16385 0.95998 0.25436 1.4371zm-0.52254 6e-3c-0.01506-3e-3 -0.0309-3e-3 -0.04606-6e-3 0.0027 0.0178 0.0027 0.0376 0.0066 0.0547 0.0041 0.0183 0.05795-0.0452 0.03949-0.0488zm-3.4715-0.23571c-0.55285-0.0297-1.1052-0.0604-1.6579-0.0908-0.0049 0.12383-0.01593 0.24917-0.04077 0.3731 0.5659-0.0891 1.1326-0.1853 1.6987-0.2823zm-9.5173 4.5312c0.0087-3e-3 -0.0093-5e-3 -0.03984-0.0105-0.02342 8e-3 -0.04688 0.0157-0.07029 0.0236 0.06848-8e-3 0.10157-0.0105 0.11013-0.0131z" 76 + fill="currentColor" 77 + /> 78 + </clipPath> 79 + <clipPath id="clipPath5-1"> 80 + <path 81 + d="m19.068 91.691-19.117 40.238 17.967 5.7393c0.03749-2.3926 0.1285-4.7939 0.1759-7.1882 0.02822-1.0804 0.05232-2.0481 0.09921-3.0946-8e-3 -5e-3 -0.01712-6e-3 -0.02468-0.0124-0.01979-0.0153-0.03518-0.0348-0.05276-0.0519-0.26915 0.14618-0.51212 0.26581-0.48745 0.14266 0.05519-0.27551 0.10598-0.57909 0.20412-0.85844l-8.85e-4 -3e-3c-0.63284-0.0144-1.2426-0.026-1.4864-0.036-0.26665-0.0109-0.53105-0.0305-0.79613-0.0481-0.04006 9e-3 -0.07621 0.0134-0.08863-6e-3 -0.02239-2e-3 -0.04494-3e-3 -0.06737-4e-3 0.05069-0.46211-0.03132-0.93415 0.1532-1.3859 0.0579-0.14164 0.2887-0.0371 0.43336-0.0502 0.61899-0.0562 1.2367-0.0964 1.8544-0.13825-0.0023-0.0699-0.0043-0.13959-0.0025-0.2092-0.09792 0.0126-0.19607 0.0277-0.29399 0.0399-0.46246 0.0576-1.0092 0.34357-1.387 0.14931-0.12433-0.0639-0.22304-0.15796-0.30884-0.26759l-0.0018-5.7e-4c-0.03928-0.0299-0.07122-0.0703-0.08666-0.12204-0.0011-3e-3 -0.0014-5e-3 -0.0026-8e-3 -0.14853-0.24491-0.23819-0.54641-0.30534-0.82811-0.02871-0.12038 0.24211-0.14354 0.36306-0.21532 0.70393-0.23847 1.4081-0.4755 2.1092-0.73038-0.596 0.0622-1.1922 0.12994-1.7894 0.20727-0.24049 0.0311-0.57954 0.26308-0.7239 0.10005-0.0027-3e-3 -0.0052-6e-3 -0.0078-9e-3 -0.12613 0.0206-0.25238 0.0443-0.37848 0.0634 0.01769-0.0555 0.0285-0.11385 0.04241-0.17074-0.68152 0.0559-1.3629 0.10863-2.0397 0.12951-0.1702 5e-3 -0.44996 0.14792-0.5065-2e-3 -0.55479-1.4675-0.05742-1.3382 0.98084-1.7643 0.627-0.21308 1.2562-0.41928 1.8847-0.62221-0.04459-0.0736-0.07861-0.15246-0.0944-0.24296-0.04145-0.23753 0.29729-0.47038 0.50465-0.65056 0.12746-0.11076 0.28722-0.16333 0.44208-0.22298-0.64666-0.0689-1.2785-0.14472-2.0704-0.28179-0.44702-0.0774-0.89832-0.14889-1.3244-0.27826-0.32338-0.0988-0.82844-0.0748-0.91431-0.40393-0.10587-0.40582 0.19247-0.92067 0.4648-1.3171 0.0998-0.14527 0.32726-0.0473 0.49033-0.0715 0.24416-0.0176 0.48816-0.0357 0.73233-0.0535 0.01049-0.26171 0.31701-0.30864 0.65389-0.29573 0.02096-0.0195 0.0337-0.0324 0.05549-0.0525 0.25322-0.23334 0.62915-0.0522 0.94364-0.0737 0.44116-0.0302 0.88079-0.053 1.3216-0.0803 0.48527-0.0295 0.96963-0.0542 1.4547-0.0816-1.8889-0.1577-3.7898-0.26764-5.7164-0.26169-1.334 4e-3 -1.062-1.366 0.27192-1.3699 3.6679-0.0107 7.2493 0.37575 10.819 0.76403 2.6717 0.33278 5.3402 0.67109 8.0168 0.98555l8.6816-18.273zm11.729 26.331c-1.9697-0.23241-3.9351-0.48043-5.901-0.72653 0.04753 0.38051-0.36531 0.84827-1.1411 0.87948-0.62713 0.0252-1.2524 0.0424-1.8785 0.0603-0.0209 0.0479-0.03809 0.0929-0.06253 0.14457-0.01435 0.0302-0.0341 0.0579-0.05402 0.0837 0.06731 0.0534 0.12401 0.10525 0.17416 0.15656 1.9487 0.11998 3.902 0.22074 5.8533 0.32954 0.32461 0.0195 0.65152 0.0322 0.97432 0.0589 0.56839 0.047 1.2497 0.1053 1.3204 0.5183zm-1.153 2.4268c-0.06617 0.0787-0.13902 0.16135-0.22234 0.24929-0.26738 0.28225-0.70683 0.23627-1.0631 0.32955-0.29502 0.0772-0.59127 0.14118-0.8872 0.20366 0.04695 0.20301 0.07747 0.40844 0.05087 0.62616-2.72e-4 2e-3 -0.0024 4e-3 -0.0028 6e-3 0.13341-0.0207 0.36698-0.14297 0.3763-0.0202 0.02545 0.33519-0.08951 0.68828-0.18985 1.0385l-9.29e-4 2e-3c0.01324 0.10724 0.01859 0.21712 0.01287 0.32882-0.0023 0.046-0.04668 0.0465-0.10325 0.0372-1.59e-4 1e-3 -0.0017 3e-3 -0.0019 4e-3 -0.89428 0.13879-1.7876 0.24923-2.6795 0.34854-0.70272 0.11603-1.4057 0.2375-2.1083 0.3555-0.19363 0.0321-0.38737 0.0631-0.58104 0.0952 0.10097 0.0177 0.20211 0.0347 0.3029 0.0528 0.26518 0.0365 0.52937 0.0751 0.79465 0.11122 0.14561 0.0386 0.42361-0.0366 0.43709 0.11771 1.39e-4 5e-3 -4.47e-4 9e-3 -7.8e-5 0.0131 0.39337 0.0879 0.96097 0.2208 1.2765 0.47355 3e-3 6.9e-4 0.0058 2e-3 0.0089 3e-3 0.20267 0.0508 1.4986 0.31264 1.545 0.59002 0.23172 1.3854-0.946 1.309-1.6518 1.3241-0.35844 8e-3 -0.71612 3e-3 -1.0734 4e-3 -0.37537-9e-3 -0.75081-0.0176-1.1263-0.0263-0.09645 0.0145-0.19319 0.0296-0.28959 0.0435 0.05305 0.0421 0.0253 0.15341-0.0079 0.26954 0.07786 0.14301-0.06732 0.43309-0.07158 0.63093-0.0089 0.41384-0.01706 0.82843-0.02589 1.2423-0.05917 1.7592-0.11603 3.5186-0.17921 5.2784 0.08198 0.47977 0.16385 0.95998 0.25436 1.4371zm-0.52254 6e-3c-0.01506-3e-3 -0.0309-3e-3 -0.04606-6e-3 0.0027 0.0178 0.0027 0.0376 0.0066 0.0547 0.0041 0.0183 0.05795-0.0452 0.03949-0.0488zm-3.4715-0.23571c-0.55285-0.0297-1.1052-0.0604-1.6579-0.0908-0.0049 0.12383-0.01593 0.24917-0.04077 0.3731 0.5659-0.0891 1.1326-0.1853 1.6987-0.2823zm-9.5173 4.5312c0.0087-3e-3 -0.0093-5e-3 -0.03984-0.0105-0.02342 8e-3 -0.04688 0.0157-0.07029 0.0236 0.06848-8e-3 0.10157-0.0105 0.11013-0.0131z" 82 + fill="currentColor" 83 + /> 84 + </clipPath> 85 + </defs> 86 + <g fill="currentColor"> 87 + <g stroke-width=".90473"> 88 + <rect 89 + x="42.633" 90 + y="43.327" 91 + width="2.6936" 92 + height="57.104" 93 + /> 94 + <rect 95 + x="75.976" 96 + y="43.158" 97 + width="2.6936" 98 + height="57.104" 99 + /> 100 + <rect 101 + x="66.096" 102 + y="24.287" 103 + width="2.6936" 104 + height="57.104" 105 + /> 106 + <rect 107 + x="52.858" 108 + y="24.342" 109 + width="2.6936" 110 + height="57.104" 111 + /> 112 + </g> 113 + <rect 114 + transform="rotate(90)" 115 + x="42.875" 116 + y="-107.06" 117 + width="2.6936" 118 + height="30.976" 119 + stroke-width=".66634" 120 + /> 121 + <rect 122 + transform="rotate(90)" 123 + x="43.178" 124 + y="-45.306" 125 + width="2.6936" 126 + height="30.976" 127 + stroke-width=".66634" 128 + /> 129 + <rect 130 + transform="rotate(-49.969)" 131 + x="33.484" 132 + y="53.784" 133 + width="2.6936" 134 + height="57.104" 135 + stroke-width=".90473" 136 + /> 137 + <rect 138 + transform="matrix(.95872 -.28437 .45549 .89024 6 4)" 139 + x="18.723" 140 + y="92.417" 141 + width="2.618" 142 + height="45.508" 143 + clip-path="url(#clipPath5)" 144 + stroke-width=".79624" 145 + /> 146 + <rect 147 + transform="matrix(-.95872 -.28437 -.45549 .89024 115.73 4.4549)" 148 + x="18.723" 149 + y="92.417" 150 + width="2.618" 151 + height="45.508" 152 + clip-path="url(#clipPath5-1)" 153 + stroke-width=".79624" 154 + /> 155 + <rect 156 + transform="matrix(-.64321 -.76569 -.76569 .64321 0 0)" 157 + x="-44.841" 158 + y="-39.447" 159 + width="2.6936" 160 + height="57.104" 161 + stroke-width=".90473" 162 + /> 163 + </g> 164 + </svg> 165 + </div> 166 + </div> 167 + <div class="relative p-1 w-12 h-full me-1 ms-1"> 168 + <!-- Reply (Comment) --> 169 + <div 170 + class="absolute bottom-1 m-2 w-12 h-12 text-center text-white align-middle bg-black hover:text-red-300 leading-12" 171 + > 172 + C 173 + </div> 174 + </div> 175 + <div class="relative p-1 w-12 h-full me-1 ms-1"> 176 + <!-- Repost (Bump) --> 177 + <div 178 + class="absolute bottom-1 m-2 w-12 h-12 text-center text-white align-middle bg-black hover:text-red-300 leading-12" 179 + > 180 + B 181 + </div> 182 + </div> 183 + </div> 184 + </div> 185 + </div> 186 + </div> 187 + <h1>Example image/media post (manually edited static html)</h1> 188 + <div 189 + class="container py-8 px-4 mx-auto mr-1 ml-1 lg:mr-auto lg:ml-auto lg:max-w-[50VW]" 190 + > 191 + <div 192 + class="overflow-hidden rounded-lg shadow-lg bg-brown-25 dark:bg-neutral-700" 193 + > 194 + <!-- --> 195 + <div class="postcontent"> 196 + <embed 197 + loading="lazy" 198 + src="https://via.placeholder.com/400x800" 199 + alt="Social Media Post" 200 + class="object-contain w-full rounded-b-none rounded-t-lg lg:max-h-[60VH]" 201 + /> 202 + <div class="p-4"> 203 + <p class="text-lg text-neutral-900 dark:text-neutral-100"> 204 + Just got my new image placeholder delivered 💖✨✨✨ 205 + </p> 206 + </div> 207 + </div> 208 + <div 209 + class="flex flex-row justify-evenly p-2 w-full border-t-2 border-b-2 border-dotted post-author-main border-neutral-700 dark:border-brown-25" 210 + > 211 + <div class="w-1/5"> 212 + <img 213 + class="w-8 h-8 rounded-full" 214 + src="/user/avatar/0" 215 + alt="Profilepicture" 216 + /> 217 + </div> 218 + <span>username@peonies.net</span> 219 + </div> 220 + <!-- --> 221 + <div class="relative w-[95%] ml-1 mr-1"> 222 + <div class="mt-4 mb-4"> 223 + <ul class="justify-between p-4 pb-1 lg:w-3/5"> 224 + <li 225 + class="inline-block p-4 mr-2 rounded-xl bg-slate-800 dark:bg-slate-400" 226 + > 227 + <a href="#" class="text-blue-300 dark:text-blue-900" 228 + >imagepost</a 229 + > 230 + </li> 231 + <li 232 + class="inline-block p-4 mr-2 rounded-xl bg-slate-800 dark:bg-slate-400" 233 + > 234 + <a href="#" class="text-blue-300 dark:text-blue-900" 235 + >placeholderpictures</a 236 + > 237 + </li> 238 + <li 239 + class="inline-block p-4 mr-2 rounded-xl bg-slate-800 dark:bg-slate-400" 240 + > 241 + <a href="#" class="text-blue-300 dark:text-blue-900" 242 + >pretty placeholders</a 243 + > 244 + </li> 245 + </ul> 246 + </div> 247 + <div 248 + class="flex float-right justify-between items-center m-4 w-min min-w-[6rem] h-fit min-h-8" 249 + > 250 + <div class="relative p-1 w-12 h-full me-1 ms-1"> 251 + <!-- Like (Push) --> 252 + <div 253 + class="absolute bottom-1 m-2 w-12 h-12 text-center text-white align-middle bg-black rounded-full hover:text-red-300 leading-12" 254 + > 255 + <svg 256 + width="100%" 257 + height="100%" 258 + viewBox="0 0 120 120" 259 + xmlns="http://www.w3.org/2000/svg" 260 + > 261 + <defs> 262 + <clipPath id="clipPath5"> 263 + <path 264 + d="m19.068 91.691-19.117 40.238 17.967 5.7393c0.03749-2.3926 0.1285-4.7939 0.1759-7.1882 0.02822-1.0804 0.05232-2.0481 0.09921-3.0946-8e-3 -5e-3 -0.01712-6e-3 -0.02468-0.0124-0.01979-0.0153-0.03518-0.0348-0.05276-0.0519-0.26915 0.14618-0.51212 0.26581-0.48745 0.14266 0.05519-0.27551 0.10598-0.57909 0.20412-0.85844l-8.85e-4 -3e-3c-0.63284-0.0144-1.2426-0.026-1.4864-0.036-0.26665-0.0109-0.53105-0.0305-0.79613-0.0481-0.04006 9e-3 -0.07621 0.0134-0.08863-6e-3 -0.02239-2e-3 -0.04494-3e-3 -0.06737-4e-3 0.05069-0.46211-0.03132-0.93415 0.1532-1.3859 0.0579-0.14164 0.2887-0.0371 0.43336-0.0502 0.61899-0.0562 1.2367-0.0964 1.8544-0.13825-0.0023-0.0699-0.0043-0.13959-0.0025-0.2092-0.09792 0.0126-0.19607 0.0277-0.29399 0.0399-0.46246 0.0576-1.0092 0.34357-1.387 0.14931-0.12433-0.0639-0.22304-0.15796-0.30884-0.26759l-0.0018-5.7e-4c-0.03928-0.0299-0.07122-0.0703-0.08666-0.12204-0.0011-3e-3 -0.0014-5e-3 -0.0026-8e-3 -0.14853-0.24491-0.23819-0.54641-0.30534-0.82811-0.02871-0.12038 0.24211-0.14354 0.36306-0.21532 0.70393-0.23847 1.4081-0.4755 2.1092-0.73038-0.596 0.0622-1.1922 0.12994-1.7894 0.20727-0.24049 0.0311-0.57954 0.26308-0.7239 0.10005-0.0027-3e-3 -0.0052-6e-3 -0.0078-9e-3 -0.12613 0.0206-0.25238 0.0443-0.37848 0.0634 0.01769-0.0555 0.0285-0.11385 0.04241-0.17074-0.68152 0.0559-1.3629 0.10863-2.0397 0.12951-0.1702 5e-3 -0.44996 0.14792-0.5065-2e-3 -0.55479-1.4675-0.05742-1.3382 0.98084-1.7643 0.627-0.21308 1.2562-0.41928 1.8847-0.62221-0.04459-0.0736-0.07861-0.15246-0.0944-0.24296-0.04145-0.23753 0.29729-0.47038 0.50465-0.65056 0.12746-0.11076 0.28722-0.16333 0.44208-0.22298-0.64666-0.0689-1.2785-0.14472-2.0704-0.28179-0.44702-0.0774-0.89832-0.14889-1.3244-0.27826-0.32338-0.0988-0.82844-0.0748-0.91431-0.40393-0.10587-0.40582 0.19247-0.92067 0.4648-1.3171 0.0998-0.14527 0.32726-0.0473 0.49033-0.0715 0.24416-0.0176 0.48816-0.0357 0.73233-0.0535 0.01049-0.26171 0.31701-0.30864 0.65389-0.29573 0.02096-0.0195 0.0337-0.0324 0.05549-0.0525 0.25322-0.23334 0.62915-0.0522 0.94364-0.0737 0.44116-0.0302 0.88079-0.053 1.3216-0.0803 0.48527-0.0295 0.96963-0.0542 1.4547-0.0816-1.8889-0.1577-3.7898-0.26764-5.7164-0.26169-1.334 4e-3 -1.062-1.366 0.27192-1.3699 3.6679-0.0107 7.2493 0.37575 10.819 0.76403 2.6717 0.33278 5.3402 0.67109 8.0168 0.98555l8.6816-18.273zm11.729 26.331c-1.9697-0.23241-3.9351-0.48043-5.901-0.72653 0.04753 0.38051-0.36531 0.84827-1.1411 0.87948-0.62713 0.0252-1.2524 0.0424-1.8785 0.0603-0.0209 0.0479-0.03809 0.0929-0.06253 0.14457-0.01435 0.0302-0.0341 0.0579-0.05402 0.0837 0.06731 0.0534 0.12401 0.10525 0.17416 0.15656 1.9487 0.11998 3.902 0.22074 5.8533 0.32954 0.32461 0.0195 0.65152 0.0322 0.97432 0.0589 0.56839 0.047 1.2497 0.1053 1.3204 0.5183zm-1.153 2.4268c-0.06617 0.0787-0.13902 0.16135-0.22234 0.24929-0.26738 0.28225-0.70683 0.23627-1.0631 0.32955-0.29502 0.0772-0.59127 0.14118-0.8872 0.20366 0.04695 0.20301 0.07747 0.40844 0.05087 0.62616-2.72e-4 2e-3 -0.0024 4e-3 -0.0028 6e-3 0.13341-0.0207 0.36698-0.14297 0.3763-0.0202 0.02545 0.33519-0.08951 0.68828-0.18985 1.0385l-9.29e-4 2e-3c0.01324 0.10724 0.01859 0.21712 0.01287 0.32882-0.0023 0.046-0.04668 0.0465-0.10325 0.0372-1.59e-4 1e-3 -0.0017 3e-3 -0.0019 4e-3 -0.89428 0.13879-1.7876 0.24923-2.6795 0.34854-0.70272 0.11603-1.4057 0.2375-2.1083 0.3555-0.19363 0.0321-0.38737 0.0631-0.58104 0.0952 0.10097 0.0177 0.20211 0.0347 0.3029 0.0528 0.26518 0.0365 0.52937 0.0751 0.79465 0.11122 0.14561 0.0386 0.42361-0.0366 0.43709 0.11771 1.39e-4 5e-3 -4.47e-4 9e-3 -7.8e-5 0.0131 0.39337 0.0879 0.96097 0.2208 1.2765 0.47355 3e-3 6.9e-4 0.0058 2e-3 0.0089 3e-3 0.20267 0.0508 1.4986 0.31264 1.545 0.59002 0.23172 1.3854-0.946 1.309-1.6518 1.3241-0.35844 8e-3 -0.71612 3e-3 -1.0734 4e-3 -0.37537-9e-3 -0.75081-0.0176-1.1263-0.0263-0.09645 0.0145-0.19319 0.0296-0.28959 0.0435 0.05305 0.0421 0.0253 0.15341-0.0079 0.26954 0.07786 0.14301-0.06732 0.43309-0.07158 0.63093-0.0089 0.41384-0.01706 0.82843-0.02589 1.2423-0.05917 1.7592-0.11603 3.5186-0.17921 5.2784 0.08198 0.47977 0.16385 0.95998 0.25436 1.4371zm-0.52254 6e-3c-0.01506-3e-3 -0.0309-3e-3 -0.04606-6e-3 0.0027 0.0178 0.0027 0.0376 0.0066 0.0547 0.0041 0.0183 0.05795-0.0452 0.03949-0.0488zm-3.4715-0.23571c-0.55285-0.0297-1.1052-0.0604-1.6579-0.0908-0.0049 0.12383-0.01593 0.24917-0.04077 0.3731 0.5659-0.0891 1.1326-0.1853 1.6987-0.2823zm-9.5173 4.5312c0.0087-3e-3 -0.0093-5e-3 -0.03984-0.0105-0.02342 8e-3 -0.04688 0.0157-0.07029 0.0236 0.06848-8e-3 0.10157-0.0105 0.11013-0.0131z" 265 + fill="currentColor" 266 + /> 267 + </clipPath> 268 + <clipPath id="clipPath5-1"> 269 + <path 270 + d="m19.068 91.691-19.117 40.238 17.967 5.7393c0.03749-2.3926 0.1285-4.7939 0.1759-7.1882 0.02822-1.0804 0.05232-2.0481 0.09921-3.0946-8e-3 -5e-3 -0.01712-6e-3 -0.02468-0.0124-0.01979-0.0153-0.03518-0.0348-0.05276-0.0519-0.26915 0.14618-0.51212 0.26581-0.48745 0.14266 0.05519-0.27551 0.10598-0.57909 0.20412-0.85844l-8.85e-4 -3e-3c-0.63284-0.0144-1.2426-0.026-1.4864-0.036-0.26665-0.0109-0.53105-0.0305-0.79613-0.0481-0.04006 9e-3 -0.07621 0.0134-0.08863-6e-3 -0.02239-2e-3 -0.04494-3e-3 -0.06737-4e-3 0.05069-0.46211-0.03132-0.93415 0.1532-1.3859 0.0579-0.14164 0.2887-0.0371 0.43336-0.0502 0.61899-0.0562 1.2367-0.0964 1.8544-0.13825-0.0023-0.0699-0.0043-0.13959-0.0025-0.2092-0.09792 0.0126-0.19607 0.0277-0.29399 0.0399-0.46246 0.0576-1.0092 0.34357-1.387 0.14931-0.12433-0.0639-0.22304-0.15796-0.30884-0.26759l-0.0018-5.7e-4c-0.03928-0.0299-0.07122-0.0703-0.08666-0.12204-0.0011-3e-3 -0.0014-5e-3 -0.0026-8e-3 -0.14853-0.24491-0.23819-0.54641-0.30534-0.82811-0.02871-0.12038 0.24211-0.14354 0.36306-0.21532 0.70393-0.23847 1.4081-0.4755 2.1092-0.73038-0.596 0.0622-1.1922 0.12994-1.7894 0.20727-0.24049 0.0311-0.57954 0.26308-0.7239 0.10005-0.0027-3e-3 -0.0052-6e-3 -0.0078-9e-3 -0.12613 0.0206-0.25238 0.0443-0.37848 0.0634 0.01769-0.0555 0.0285-0.11385 0.04241-0.17074-0.68152 0.0559-1.3629 0.10863-2.0397 0.12951-0.1702 5e-3 -0.44996 0.14792-0.5065-2e-3 -0.55479-1.4675-0.05742-1.3382 0.98084-1.7643 0.627-0.21308 1.2562-0.41928 1.8847-0.62221-0.04459-0.0736-0.07861-0.15246-0.0944-0.24296-0.04145-0.23753 0.29729-0.47038 0.50465-0.65056 0.12746-0.11076 0.28722-0.16333 0.44208-0.22298-0.64666-0.0689-1.2785-0.14472-2.0704-0.28179-0.44702-0.0774-0.89832-0.14889-1.3244-0.27826-0.32338-0.0988-0.82844-0.0748-0.91431-0.40393-0.10587-0.40582 0.19247-0.92067 0.4648-1.3171 0.0998-0.14527 0.32726-0.0473 0.49033-0.0715 0.24416-0.0176 0.48816-0.0357 0.73233-0.0535 0.01049-0.26171 0.31701-0.30864 0.65389-0.29573 0.02096-0.0195 0.0337-0.0324 0.05549-0.0525 0.25322-0.23334 0.62915-0.0522 0.94364-0.0737 0.44116-0.0302 0.88079-0.053 1.3216-0.0803 0.48527-0.0295 0.96963-0.0542 1.4547-0.0816-1.8889-0.1577-3.7898-0.26764-5.7164-0.26169-1.334 4e-3 -1.062-1.366 0.27192-1.3699 3.6679-0.0107 7.2493 0.37575 10.819 0.76403 2.6717 0.33278 5.3402 0.67109 8.0168 0.98555l8.6816-18.273zm11.729 26.331c-1.9697-0.23241-3.9351-0.48043-5.901-0.72653 0.04753 0.38051-0.36531 0.84827-1.1411 0.87948-0.62713 0.0252-1.2524 0.0424-1.8785 0.0603-0.0209 0.0479-0.03809 0.0929-0.06253 0.14457-0.01435 0.0302-0.0341 0.0579-0.05402 0.0837 0.06731 0.0534 0.12401 0.10525 0.17416 0.15656 1.9487 0.11998 3.902 0.22074 5.8533 0.32954 0.32461 0.0195 0.65152 0.0322 0.97432 0.0589 0.56839 0.047 1.2497 0.1053 1.3204 0.5183zm-1.153 2.4268c-0.06617 0.0787-0.13902 0.16135-0.22234 0.24929-0.26738 0.28225-0.70683 0.23627-1.0631 0.32955-0.29502 0.0772-0.59127 0.14118-0.8872 0.20366 0.04695 0.20301 0.07747 0.40844 0.05087 0.62616-2.72e-4 2e-3 -0.0024 4e-3 -0.0028 6e-3 0.13341-0.0207 0.36698-0.14297 0.3763-0.0202 0.02545 0.33519-0.08951 0.68828-0.18985 1.0385l-9.29e-4 2e-3c0.01324 0.10724 0.01859 0.21712 0.01287 0.32882-0.0023 0.046-0.04668 0.0465-0.10325 0.0372-1.59e-4 1e-3 -0.0017 3e-3 -0.0019 4e-3 -0.89428 0.13879-1.7876 0.24923-2.6795 0.34854-0.70272 0.11603-1.4057 0.2375-2.1083 0.3555-0.19363 0.0321-0.38737 0.0631-0.58104 0.0952 0.10097 0.0177 0.20211 0.0347 0.3029 0.0528 0.26518 0.0365 0.52937 0.0751 0.79465 0.11122 0.14561 0.0386 0.42361-0.0366 0.43709 0.11771 1.39e-4 5e-3 -4.47e-4 9e-3 -7.8e-5 0.0131 0.39337 0.0879 0.96097 0.2208 1.2765 0.47355 3e-3 6.9e-4 0.0058 2e-3 0.0089 3e-3 0.20267 0.0508 1.4986 0.31264 1.545 0.59002 0.23172 1.3854-0.946 1.309-1.6518 1.3241-0.35844 8e-3 -0.71612 3e-3 -1.0734 4e-3 -0.37537-9e-3 -0.75081-0.0176-1.1263-0.0263-0.09645 0.0145-0.19319 0.0296-0.28959 0.0435 0.05305 0.0421 0.0253 0.15341-0.0079 0.26954 0.07786 0.14301-0.06732 0.43309-0.07158 0.63093-0.0089 0.41384-0.01706 0.82843-0.02589 1.2423-0.05917 1.7592-0.11603 3.5186-0.17921 5.2784 0.08198 0.47977 0.16385 0.95998 0.25436 1.4371zm-0.52254 6e-3c-0.01506-3e-3 -0.0309-3e-3 -0.04606-6e-3 0.0027 0.0178 0.0027 0.0376 0.0066 0.0547 0.0041 0.0183 0.05795-0.0452 0.03949-0.0488zm-3.4715-0.23571c-0.55285-0.0297-1.1052-0.0604-1.6579-0.0908-0.0049 0.12383-0.01593 0.24917-0.04077 0.3731 0.5659-0.0891 1.1326-0.1853 1.6987-0.2823zm-9.5173 4.5312c0.0087-3e-3 -0.0093-5e-3 -0.03984-0.0105-0.02342 8e-3 -0.04688 0.0157-0.07029 0.0236 0.06848-8e-3 0.10157-0.0105 0.11013-0.0131z" 271 + fill="currentColor" 272 + /> 273 + </clipPath> 274 + </defs> 275 + <g fill="currentColor"> 276 + <g stroke-width=".90473"> 277 + <rect 278 + x="42.633" 279 + y="43.327" 280 + width="2.6936" 281 + height="57.104" 282 + /> 283 + <rect 284 + x="75.976" 285 + y="43.158" 286 + width="2.6936" 287 + height="57.104" 288 + /> 289 + <rect 290 + x="66.096" 291 + y="24.287" 292 + width="2.6936" 293 + height="57.104" 294 + /> 295 + <rect 296 + x="52.858" 297 + y="24.342" 298 + width="2.6936" 299 + height="57.104" 300 + /> 301 + </g> 302 + <rect 303 + transform="rotate(90)" 304 + x="42.875" 305 + y="-107.06" 306 + width="2.6936" 307 + height="30.976" 308 + stroke-width=".66634" 309 + /> 310 + <rect 311 + transform="rotate(90)" 312 + x="43.178" 313 + y="-45.306" 314 + width="2.6936" 315 + height="30.976" 316 + stroke-width=".66634" 317 + /> 318 + <rect 319 + transform="rotate(-49.969)" 320 + x="33.484" 321 + y="53.784" 322 + width="2.6936" 323 + height="57.104" 324 + stroke-width=".90473" 325 + /> 326 + <rect 327 + transform="matrix(.95872 -.28437 .45549 .89024 6 4)" 328 + x="18.723" 329 + y="92.417" 330 + width="2.618" 331 + height="45.508" 332 + clip-path="url(#clipPath5)" 333 + stroke-width=".79624" 334 + /> 335 + <rect 336 + transform="matrix(-.95872 -.28437 -.45549 .89024 115.73 4.4549)" 337 + x="18.723" 338 + y="92.417" 339 + width="2.618" 340 + height="45.508" 341 + clip-path="url(#clipPath5-1)" 342 + stroke-width=".79624" 343 + /> 344 + <rect 345 + transform="matrix(-.64321 -.76569 -.76569 .64321 0 0)" 346 + x="-44.841" 347 + y="-39.447" 348 + width="2.6936" 349 + height="57.104" 350 + stroke-width=".90473" 351 + /> 352 + </g> 353 + </svg> 354 + </div> 355 + </div> 356 + <div class="relative p-1 w-12 h-full me-1 ms-1"> 357 + <!-- Reply (Comment) --> 358 + <div 359 + class="absolute bottom-1 m-2 w-12 h-12 text-center text-white align-middle bg-black hover:text-red-300 leading-12" 360 + > 361 + C 362 + </div> 363 + </div> 364 + <div class="relative p-1 w-12 h-full me-1 ms-1"> 365 + <!-- Repost (Bump) --> 366 + <div 367 + class="absolute bottom-1 m-2 w-12 h-12 text-center text-white align-middle bg-black hover:text-red-300 leading-12" 368 + > 369 + B 370 + </div> 371 + </div> 372 + </div> 373 + </div> 374 + </div> 375 + </div> 376 + <h1>Example article post (manually edited static html)</h1> 377 + <div 378 + class="container py-8 px-4 mx-auto mr-1 ml-1 lg:mr-auto lg:ml-auto lg:max-w-[50VW]" 379 + > 380 + <div 381 + class="overflow-hidden rounded-lg shadow-lg bg-brown-25 dark:bg-neutral-700" 382 + > 383 + <!-- --> 384 + <h3 class="p-4">My latin learning progress</h3> 385 + <!-- --> 386 + <div 387 + class="flex flex-row justify-evenly p-2 w-full border-t-2 border-b-2 border-dotted post-author-main border-neutral-700 dark:border-brown-25" 388 + > 389 + <div class="w-1/5"> 390 + <img 391 + class="w-8 h-8 rounded-full" 392 + src="/user/avatar/1" 393 + alt="Profilepicture" 394 + /> 395 + </div> 396 + <span>username@peonies.net</span> 397 + </div> 398 + <!-- --> 399 + <div 400 + class="overflow-y-auto p-4 border-b-2 border-dotted postcontent text-neutral-900 articlecontent h-fit border-neutral-700 lg:max-h-[80VH] dark:text-neutral-100 dark:border-brown-25" 401 + > 402 + <p> 403 + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do 404 + eiusmod tempor incididunt ut labore et dolore magna aliqua. 405 + Posuere lorem ipsum dolor sit amet. Porttitor eget dolor morbi 406 + non arcu risus quis varius. Fames ac turpis egestas sed tempus. 407 + Pulvinar etiam non quam lacus suspendisse faucibus interdum 408 + posuere. Euismod in pellentesque massa placerat duis ultricies 409 + lacus sed. Nullam eget felis eget nunc lobortis mattis aliquam 410 + faucibus purus. Nullam eget felis eget nunc lobortis. Amet 411 + consectetur adipiscing elit pellentesque habitant morbi 412 + tristique senectus. Sed turpis tincidunt id aliquet risus 413 + feugiat in. Ut consequat semper viverra nam. Sociis natoque 414 + penatibus et magnis dis parturient montes nascetur. Imperdiet 415 + massa tincidunt nunc pulvinar sapien et. Lorem mollis aliquam ut 416 + porttitor leo a diam. 417 + </p> 418 + 419 + <p> 420 + In fermentum et sollicitudin ac. Tellus id interdum velit 421 + laoreet id. Nibh cras pulvinar mattis nunc. 422 + <b 423 + >Interdum velit laoreet id donec ultrices tincidunt arcu. Ac 424 + tortor dignissim convallis aenean.</b 425 + > 426 + Sit amet nulla facilisi morbi tempus. Nec feugiat in fermentum 427 + posuere urna nec tincidunt praesent. Nunc mi ipsum faucibus 428 + vitae aliquet nec ullamcorper sit amet. At augue eget arcu 429 + dictum varius. Quam pellentesque nec nam aliquam sem et tortor 430 + consequat. Duis tristique sollicitudin nibh sit amet commodo. 431 + </p> 432 + </div> 433 + <!-- --> 434 + <div class="relative w-[95%] ml-1 mr-1"> 435 + <div class="mt-4 mb-4"> 436 + <ul class="justify-between p-4 pb-1 lg:w-3/5"> 437 + <li 438 + class="inline-block p-4 mr-2 rounded-xl bg-slate-800 dark:bg-slate-400" 439 + > 440 + <a href="#" class="text-blue-300 dark:text-blue-900" 441 + >Languagelearning</a 442 + > 443 + </li> 444 + <li 445 + class="inline-block p-4 mr-2 rounded-xl bg-slate-800 dark:bg-slate-400" 446 + > 447 + <a href="#" class="text-blue-300 dark:text-blue-900" 448 + >Duolingo</a 449 + > 450 + </li> 451 + <li 452 + class="inline-block p-4 mr-2 rounded-xl bg-slate-800 dark:bg-slate-400" 453 + > 454 + <a href="#" class="text-blue-300 dark:text-blue-900" 455 + >Latin</a 456 + > 457 + </li> 458 + <li 459 + class="inline-block p-4 mr-2 rounded-xl bg-slate-800 dark:bg-slate-400" 460 + > 461 + <a href="#" class="text-blue-300 dark:text-blue-900" 462 + >Typewriters unite</a 463 + > 464 + </li> 465 + </ul> 466 + </div> 467 + <div 468 + class="flex float-right justify-between items-center m-4 w-min min-w-[6rem] h-fit min-h-8" 469 + > 470 + <div class="relative p-1 w-12 h-full me-1 ms-1"> 471 + <!-- Like (Push) --> 472 + <div 473 + class="absolute bottom-1 m-2 w-12 h-12 text-center text-white align-middle bg-black rounded-full hover:text-red-300 leading-12" 474 + > 475 + <svg 476 + width="100%" 477 + height="100%" 478 + viewBox="0 0 120 120" 479 + xmlns="http://www.w3.org/2000/svg" 480 + > 481 + <defs> 482 + <clipPath id="clipPath5"> 483 + <path 484 + d="m19.068 91.691-19.117 40.238 17.967 5.7393c0.03749-2.3926 0.1285-4.7939 0.1759-7.1882 0.02822-1.0804 0.05232-2.0481 0.09921-3.0946-8e-3 -5e-3 -0.01712-6e-3 -0.02468-0.0124-0.01979-0.0153-0.03518-0.0348-0.05276-0.0519-0.26915 0.14618-0.51212 0.26581-0.48745 0.14266 0.05519-0.27551 0.10598-0.57909 0.20412-0.85844l-8.85e-4 -3e-3c-0.63284-0.0144-1.2426-0.026-1.4864-0.036-0.26665-0.0109-0.53105-0.0305-0.79613-0.0481-0.04006 9e-3 -0.07621 0.0134-0.08863-6e-3 -0.02239-2e-3 -0.04494-3e-3 -0.06737-4e-3 0.05069-0.46211-0.03132-0.93415 0.1532-1.3859 0.0579-0.14164 0.2887-0.0371 0.43336-0.0502 0.61899-0.0562 1.2367-0.0964 1.8544-0.13825-0.0023-0.0699-0.0043-0.13959-0.0025-0.2092-0.09792 0.0126-0.19607 0.0277-0.29399 0.0399-0.46246 0.0576-1.0092 0.34357-1.387 0.14931-0.12433-0.0639-0.22304-0.15796-0.30884-0.26759l-0.0018-5.7e-4c-0.03928-0.0299-0.07122-0.0703-0.08666-0.12204-0.0011-3e-3 -0.0014-5e-3 -0.0026-8e-3 -0.14853-0.24491-0.23819-0.54641-0.30534-0.82811-0.02871-0.12038 0.24211-0.14354 0.36306-0.21532 0.70393-0.23847 1.4081-0.4755 2.1092-0.73038-0.596 0.0622-1.1922 0.12994-1.7894 0.20727-0.24049 0.0311-0.57954 0.26308-0.7239 0.10005-0.0027-3e-3 -0.0052-6e-3 -0.0078-9e-3 -0.12613 0.0206-0.25238 0.0443-0.37848 0.0634 0.01769-0.0555 0.0285-0.11385 0.04241-0.17074-0.68152 0.0559-1.3629 0.10863-2.0397 0.12951-0.1702 5e-3 -0.44996 0.14792-0.5065-2e-3 -0.55479-1.4675-0.05742-1.3382 0.98084-1.7643 0.627-0.21308 1.2562-0.41928 1.8847-0.62221-0.04459-0.0736-0.07861-0.15246-0.0944-0.24296-0.04145-0.23753 0.29729-0.47038 0.50465-0.65056 0.12746-0.11076 0.28722-0.16333 0.44208-0.22298-0.64666-0.0689-1.2785-0.14472-2.0704-0.28179-0.44702-0.0774-0.89832-0.14889-1.3244-0.27826-0.32338-0.0988-0.82844-0.0748-0.91431-0.40393-0.10587-0.40582 0.19247-0.92067 0.4648-1.3171 0.0998-0.14527 0.32726-0.0473 0.49033-0.0715 0.24416-0.0176 0.48816-0.0357 0.73233-0.0535 0.01049-0.26171 0.31701-0.30864 0.65389-0.29573 0.02096-0.0195 0.0337-0.0324 0.05549-0.0525 0.25322-0.23334 0.62915-0.0522 0.94364-0.0737 0.44116-0.0302 0.88079-0.053 1.3216-0.0803 0.48527-0.0295 0.96963-0.0542 1.4547-0.0816-1.8889-0.1577-3.7898-0.26764-5.7164-0.26169-1.334 4e-3 -1.062-1.366 0.27192-1.3699 3.6679-0.0107 7.2493 0.37575 10.819 0.76403 2.6717 0.33278 5.3402 0.67109 8.0168 0.98555l8.6816-18.273zm11.729 26.331c-1.9697-0.23241-3.9351-0.48043-5.901-0.72653 0.04753 0.38051-0.36531 0.84827-1.1411 0.87948-0.62713 0.0252-1.2524 0.0424-1.8785 0.0603-0.0209 0.0479-0.03809 0.0929-0.06253 0.14457-0.01435 0.0302-0.0341 0.0579-0.05402 0.0837 0.06731 0.0534 0.12401 0.10525 0.17416 0.15656 1.9487 0.11998 3.902 0.22074 5.8533 0.32954 0.32461 0.0195 0.65152 0.0322 0.97432 0.0589 0.56839 0.047 1.2497 0.1053 1.3204 0.5183zm-1.153 2.4268c-0.06617 0.0787-0.13902 0.16135-0.22234 0.24929-0.26738 0.28225-0.70683 0.23627-1.0631 0.32955-0.29502 0.0772-0.59127 0.14118-0.8872 0.20366 0.04695 0.20301 0.07747 0.40844 0.05087 0.62616-2.72e-4 2e-3 -0.0024 4e-3 -0.0028 6e-3 0.13341-0.0207 0.36698-0.14297 0.3763-0.0202 0.02545 0.33519-0.08951 0.68828-0.18985 1.0385l-9.29e-4 2e-3c0.01324 0.10724 0.01859 0.21712 0.01287 0.32882-0.0023 0.046-0.04668 0.0465-0.10325 0.0372-1.59e-4 1e-3 -0.0017 3e-3 -0.0019 4e-3 -0.89428 0.13879-1.7876 0.24923-2.6795 0.34854-0.70272 0.11603-1.4057 0.2375-2.1083 0.3555-0.19363 0.0321-0.38737 0.0631-0.58104 0.0952 0.10097 0.0177 0.20211 0.0347 0.3029 0.0528 0.26518 0.0365 0.52937 0.0751 0.79465 0.11122 0.14561 0.0386 0.42361-0.0366 0.43709 0.11771 1.39e-4 5e-3 -4.47e-4 9e-3 -7.8e-5 0.0131 0.39337 0.0879 0.96097 0.2208 1.2765 0.47355 3e-3 6.9e-4 0.0058 2e-3 0.0089 3e-3 0.20267 0.0508 1.4986 0.31264 1.545 0.59002 0.23172 1.3854-0.946 1.309-1.6518 1.3241-0.35844 8e-3 -0.71612 3e-3 -1.0734 4e-3 -0.37537-9e-3 -0.75081-0.0176-1.1263-0.0263-0.09645 0.0145-0.19319 0.0296-0.28959 0.0435 0.05305 0.0421 0.0253 0.15341-0.0079 0.26954 0.07786 0.14301-0.06732 0.43309-0.07158 0.63093-0.0089 0.41384-0.01706 0.82843-0.02589 1.2423-0.05917 1.7592-0.11603 3.5186-0.17921 5.2784 0.08198 0.47977 0.16385 0.95998 0.25436 1.4371zm-0.52254 6e-3c-0.01506-3e-3 -0.0309-3e-3 -0.04606-6e-3 0.0027 0.0178 0.0027 0.0376 0.0066 0.0547 0.0041 0.0183 0.05795-0.0452 0.03949-0.0488zm-3.4715-0.23571c-0.55285-0.0297-1.1052-0.0604-1.6579-0.0908-0.0049 0.12383-0.01593 0.24917-0.04077 0.3731 0.5659-0.0891 1.1326-0.1853 1.6987-0.2823zm-9.5173 4.5312c0.0087-3e-3 -0.0093-5e-3 -0.03984-0.0105-0.02342 8e-3 -0.04688 0.0157-0.07029 0.0236 0.06848-8e-3 0.10157-0.0105 0.11013-0.0131z" 485 + fill="currentColor" 486 + /> 487 + </clipPath> 488 + <clipPath id="clipPath5-1"> 489 + <path 490 + d="m19.068 91.691-19.117 40.238 17.967 5.7393c0.03749-2.3926 0.1285-4.7939 0.1759-7.1882 0.02822-1.0804 0.05232-2.0481 0.09921-3.0946-8e-3 -5e-3 -0.01712-6e-3 -0.02468-0.0124-0.01979-0.0153-0.03518-0.0348-0.05276-0.0519-0.26915 0.14618-0.51212 0.26581-0.48745 0.14266 0.05519-0.27551 0.10598-0.57909 0.20412-0.85844l-8.85e-4 -3e-3c-0.63284-0.0144-1.2426-0.026-1.4864-0.036-0.26665-0.0109-0.53105-0.0305-0.79613-0.0481-0.04006 9e-3 -0.07621 0.0134-0.08863-6e-3 -0.02239-2e-3 -0.04494-3e-3 -0.06737-4e-3 0.05069-0.46211-0.03132-0.93415 0.1532-1.3859 0.0579-0.14164 0.2887-0.0371 0.43336-0.0502 0.61899-0.0562 1.2367-0.0964 1.8544-0.13825-0.0023-0.0699-0.0043-0.13959-0.0025-0.2092-0.09792 0.0126-0.19607 0.0277-0.29399 0.0399-0.46246 0.0576-1.0092 0.34357-1.387 0.14931-0.12433-0.0639-0.22304-0.15796-0.30884-0.26759l-0.0018-5.7e-4c-0.03928-0.0299-0.07122-0.0703-0.08666-0.12204-0.0011-3e-3 -0.0014-5e-3 -0.0026-8e-3 -0.14853-0.24491-0.23819-0.54641-0.30534-0.82811-0.02871-0.12038 0.24211-0.14354 0.36306-0.21532 0.70393-0.23847 1.4081-0.4755 2.1092-0.73038-0.596 0.0622-1.1922 0.12994-1.7894 0.20727-0.24049 0.0311-0.57954 0.26308-0.7239 0.10005-0.0027-3e-3 -0.0052-6e-3 -0.0078-9e-3 -0.12613 0.0206-0.25238 0.0443-0.37848 0.0634 0.01769-0.0555 0.0285-0.11385 0.04241-0.17074-0.68152 0.0559-1.3629 0.10863-2.0397 0.12951-0.1702 5e-3 -0.44996 0.14792-0.5065-2e-3 -0.55479-1.4675-0.05742-1.3382 0.98084-1.7643 0.627-0.21308 1.2562-0.41928 1.8847-0.62221-0.04459-0.0736-0.07861-0.15246-0.0944-0.24296-0.04145-0.23753 0.29729-0.47038 0.50465-0.65056 0.12746-0.11076 0.28722-0.16333 0.44208-0.22298-0.64666-0.0689-1.2785-0.14472-2.0704-0.28179-0.44702-0.0774-0.89832-0.14889-1.3244-0.27826-0.32338-0.0988-0.82844-0.0748-0.91431-0.40393-0.10587-0.40582 0.19247-0.92067 0.4648-1.3171 0.0998-0.14527 0.32726-0.0473 0.49033-0.0715 0.24416-0.0176 0.48816-0.0357 0.73233-0.0535 0.01049-0.26171 0.31701-0.30864 0.65389-0.29573 0.02096-0.0195 0.0337-0.0324 0.05549-0.0525 0.25322-0.23334 0.62915-0.0522 0.94364-0.0737 0.44116-0.0302 0.88079-0.053 1.3216-0.0803 0.48527-0.0295 0.96963-0.0542 1.4547-0.0816-1.8889-0.1577-3.7898-0.26764-5.7164-0.26169-1.334 4e-3 -1.062-1.366 0.27192-1.3699 3.6679-0.0107 7.2493 0.37575 10.819 0.76403 2.6717 0.33278 5.3402 0.67109 8.0168 0.98555l8.6816-18.273zm11.729 26.331c-1.9697-0.23241-3.9351-0.48043-5.901-0.72653 0.04753 0.38051-0.36531 0.84827-1.1411 0.87948-0.62713 0.0252-1.2524 0.0424-1.8785 0.0603-0.0209 0.0479-0.03809 0.0929-0.06253 0.14457-0.01435 0.0302-0.0341 0.0579-0.05402 0.0837 0.06731 0.0534 0.12401 0.10525 0.17416 0.15656 1.9487 0.11998 3.902 0.22074 5.8533 0.32954 0.32461 0.0195 0.65152 0.0322 0.97432 0.0589 0.56839 0.047 1.2497 0.1053 1.3204 0.5183zm-1.153 2.4268c-0.06617 0.0787-0.13902 0.16135-0.22234 0.24929-0.26738 0.28225-0.70683 0.23627-1.0631 0.32955-0.29502 0.0772-0.59127 0.14118-0.8872 0.20366 0.04695 0.20301 0.07747 0.40844 0.05087 0.62616-2.72e-4 2e-3 -0.0024 4e-3 -0.0028 6e-3 0.13341-0.0207 0.36698-0.14297 0.3763-0.0202 0.02545 0.33519-0.08951 0.68828-0.18985 1.0385l-9.29e-4 2e-3c0.01324 0.10724 0.01859 0.21712 0.01287 0.32882-0.0023 0.046-0.04668 0.0465-0.10325 0.0372-1.59e-4 1e-3 -0.0017 3e-3 -0.0019 4e-3 -0.89428 0.13879-1.7876 0.24923-2.6795 0.34854-0.70272 0.11603-1.4057 0.2375-2.1083 0.3555-0.19363 0.0321-0.38737 0.0631-0.58104 0.0952 0.10097 0.0177 0.20211 0.0347 0.3029 0.0528 0.26518 0.0365 0.52937 0.0751 0.79465 0.11122 0.14561 0.0386 0.42361-0.0366 0.43709 0.11771 1.39e-4 5e-3 -4.47e-4 9e-3 -7.8e-5 0.0131 0.39337 0.0879 0.96097 0.2208 1.2765 0.47355 3e-3 6.9e-4 0.0058 2e-3 0.0089 3e-3 0.20267 0.0508 1.4986 0.31264 1.545 0.59002 0.23172 1.3854-0.946 1.309-1.6518 1.3241-0.35844 8e-3 -0.71612 3e-3 -1.0734 4e-3 -0.37537-9e-3 -0.75081-0.0176-1.1263-0.0263-0.09645 0.0145-0.19319 0.0296-0.28959 0.0435 0.05305 0.0421 0.0253 0.15341-0.0079 0.26954 0.07786 0.14301-0.06732 0.43309-0.07158 0.63093-0.0089 0.41384-0.01706 0.82843-0.02589 1.2423-0.05917 1.7592-0.11603 3.5186-0.17921 5.2784 0.08198 0.47977 0.16385 0.95998 0.25436 1.4371zm-0.52254 6e-3c-0.01506-3e-3 -0.0309-3e-3 -0.04606-6e-3 0.0027 0.0178 0.0027 0.0376 0.0066 0.0547 0.0041 0.0183 0.05795-0.0452 0.03949-0.0488zm-3.4715-0.23571c-0.55285-0.0297-1.1052-0.0604-1.6579-0.0908-0.0049 0.12383-0.01593 0.24917-0.04077 0.3731 0.5659-0.0891 1.1326-0.1853 1.6987-0.2823zm-9.5173 4.5312c0.0087-3e-3 -0.0093-5e-3 -0.03984-0.0105-0.02342 8e-3 -0.04688 0.0157-0.07029 0.0236 0.06848-8e-3 0.10157-0.0105 0.11013-0.0131z" 491 + fill="currentColor" 492 + /> 493 + </clipPath> 494 + </defs> 495 + <g fill="currentColor"> 496 + <g stroke-width=".90473"> 497 + <rect 498 + x="42.633" 499 + y="43.327" 500 + width="2.6936" 501 + height="57.104" 502 + /> 503 + <rect 504 + x="75.976" 505 + y="43.158" 506 + width="2.6936" 507 + height="57.104" 508 + /> 509 + <rect 510 + x="66.096" 511 + y="24.287" 512 + width="2.6936" 513 + height="57.104" 514 + /> 515 + <rect 516 + x="52.858" 517 + y="24.342" 518 + width="2.6936" 519 + height="57.104" 520 + /> 521 + </g> 522 + <rect 523 + transform="rotate(90)" 524 + x="42.875" 525 + y="-107.06" 526 + width="2.6936" 527 + height="30.976" 528 + stroke-width=".66634" 529 + /> 530 + <rect 531 + transform="rotate(90)" 532 + x="43.178" 533 + y="-45.306" 534 + width="2.6936" 535 + height="30.976" 536 + stroke-width=".66634" 537 + /> 538 + <rect 539 + transform="rotate(-49.969)" 540 + x="33.484" 541 + y="53.784" 542 + width="2.6936" 543 + height="57.104" 544 + stroke-width=".90473" 545 + /> 546 + <rect 547 + transform="matrix(.95872 -.28437 .45549 .89024 6 4)" 548 + x="18.723" 549 + y="92.417" 550 + width="2.618" 551 + height="45.508" 552 + clip-path="url(#clipPath5)" 553 + stroke-width=".79624" 554 + /> 555 + <rect 556 + transform="matrix(-.95872 -.28437 -.45549 .89024 115.73 4.4549)" 557 + x="18.723" 558 + y="92.417" 559 + width="2.618" 560 + height="45.508" 561 + clip-path="url(#clipPath5-1)" 562 + stroke-width=".79624" 563 + /> 564 + <rect 565 + transform="matrix(-.64321 -.76569 -.76569 .64321 0 0)" 566 + x="-44.841" 567 + y="-39.447" 568 + width="2.6936" 569 + height="57.104" 570 + stroke-width=".90473" 571 + /> 572 + </g> 573 + </svg> 574 + </div> 575 + </div> 576 + <div class="relative p-1 w-12 h-full me-1 ms-1"> 577 + <!-- Reply (Comment) --> 578 + <div 579 + class="absolute bottom-1 m-2 w-12 h-12 text-center text-white align-middle bg-black hover:text-red-300 leading-12" 580 + > 581 + C 582 + </div> 583 + </div> 584 + <div class="relative p-1 w-12 h-full me-1 ms-1"> 585 + <!-- Repost (Bump) --> 586 + <div 587 + class="absolute bottom-1 m-2 w-12 h-12 text-center text-white align-middle bg-black hover:text-red-300 leading-12" 588 + > 589 + B 590 + </div> 591 + </div> 592 + </div> 593 + </div> 594 + </div> 595 + </div>
+431
backend-rs/frontend_assets/html/home.html
··· 1 + <!-- 2 + ~ Copyright (c) 2024, MLC 'Strawmelonjuice' Bloeiman 3 + ~ 4 + ~ Licensed under the BSD 3-Clause License. See the LICENSE file for more info. 5 + --> 6 + <!doctype html> 7 + <html lang="en"> 8 + <head> 9 + <title>Home - Lumina(@{{iid}})</title> 10 + <meta charset="UTF-8" /> 11 + <style></style> 12 + <script src="/app.js" type="module"></script> 13 + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 14 + </head> 15 + 16 + <body 17 + class="p-0 m-0 w-screen h-screen overflow-clip max-h-[10VH] bg-brown-100 dark:bg-neutral-700" 18 + > 19 + <nav 20 + class="rounded-b-lg bg-emerald-200 lg:rounded-b-none dark:bg-teal-800" 21 + > 22 + <div class="px-2 mx-auto max-w-7xl sm:px-6 lg:px-8"> 23 + <div class="relative flex items-center justify-between h-16"> 24 + <div 25 + class="absolute inset-y-0 left-0 flex items-center sm:hidden" 26 + > 27 + <!-- Mobile menu button--> 28 + <button 29 + type="button" 30 + class="relative inline-flex items-center justify-center p-2 text-gray-400 rounded-md hover:text-white hover:bg-gray-700 focus:ring-2 focus:ring-inset focus:ring-white focus:outline-none" 31 + aria-controls="mobile-menu" 32 + aria-expanded="false" 33 + id="btn-mobile-menu" 34 + > 35 + <span class="absolute -inset-0.5"></span> 36 + <span class="sr-only">Open main menu</span> 37 + <svg 38 + class="block w-6 h-6" 39 + fill="none" 40 + viewBox="0 0 24 24" 41 + stroke-width="1.5" 42 + stroke="currentColor" 43 + aria-hidden="true" 44 + id="btn-mobile-menu-open" 45 + > 46 + <path 47 + stroke-linecap="round" 48 + stroke-linejoin="round" 49 + d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" 50 + /> 51 + </svg> 52 + <svg 53 + class="w-6 h-6" 54 + fill="none" 55 + viewBox="0 0 24 24" 56 + stroke-width="1.5" 57 + stroke="currentColor" 58 + aria-hidden="true" 59 + id="btn-mobile-menu-close" 60 + > 61 + <path 62 + stroke-linecap="round" 63 + stroke-linejoin="round" 64 + d="M6 18L18 6M6 6l12 12" 65 + /> 66 + </svg> 67 + </button> 68 + </div> 69 + <div 70 + class="flex items-center justify-center flex-1 sm:justify-start sm:items-stretch" 71 + > 72 + <div class="flex items-center flex-shrink-0"> 73 + <img 74 + class="w-auto h-10 rounded-md bg-opacity-60 border-amber-600 bg-stone-100 dark:bg-stone-100" 75 + src="/logo.svg" 76 + alt="Lumina Instance" 77 + /> 78 + </div> 79 + <div class="hidden sm:block sm:ml-6"> 80 + <div class="flex space-x-4"> 81 + <!-- Current: "bg-gray-900 text-white", Default: "text-gray-300 hover:bg-gray-700 hover:text-white" --> 82 + <a 83 + href="javascript:void(0)" 84 + class="px-3 py-2 text-sm font-medium text-white bg-gray-900 border-2 rounded-md" 85 + aria-current="page" 86 + id="home-nav" 87 + >Home</a 88 + > 89 + <a 90 + href="javascript:void(0)" 91 + class="px-3 py-2 text-sm font-medium bg-orange-200 border-2 rounded-md border-emerald-600 dark:text-orange-200 dark:bg-yellow-700 hover:text-white hover:bg-gray-700 text-brown-800 dark:border-zinc-400" 92 + id="test-nav" 93 + >Tests for post rendering</a 94 + > 95 + <a 96 + href="javascript:void(0)" 97 + class="px-3 py-2 text-sm font-medium bg-orange-200 border-2 rounded-md border-emerald-600 dark:text-orange-200 dark:bg-yellow-700 hover:text-white hover:bg-gray-700 text-brown-800 dark:border-zinc-400" 98 + id="notifications-nav" 99 + ><svg 100 + xmlns="http://www.w3.org/2000/svg" 101 + id="svg1" 102 + fill="none" 103 + stroke="currentColor" 104 + stroke-width="1.5" 105 + aria-hidden="true" 106 + class="inline w-6 h-6" 107 + viewBox="0 0 24 24" 108 + > 109 + <path 110 + id="path1" 111 + stroke-linecap="round" 112 + stroke-linejoin="round" 113 + d="M14.857 17.082a23.848 23.848 0 005.454-1.31A8.967 8.967 0 0118 9.75v-.7V9A6 6 0 006 9v.75a8.967 8.967 0 01-2.312 6.022c1.733.64 3.56 1.085 5.455 1.31m5.714 0a24.255 24.255 0 01-5.714 0m5.714 0a3 3 0 11-5.714 0" 114 + /> 115 + <ellipse 116 + style=" 117 + stroke-opacity: 1; 118 + stroke-dasharray: none; 119 + stroke-width: 0.9; 120 + stroke: #000; 121 + fill-rule: evenodd; 122 + fill: red; 123 + " 124 + id="path2" 125 + cx="16.526" 126 + cy="19.078" 127 + class="svg_activenotification" 128 + rx="1.919" 129 + ry="1.817" 130 + /></svg 131 + >&nbsp;Notifications</a 132 + > 133 + </div> 134 + </div> 135 + </div> 136 + <div 137 + class="absolute inset-y-0 right-0 flex items-center pr-2 sm:static sm:inset-auto sm:pr-0 sm:ml-6" 138 + > 139 + <button 140 + type="button" 141 + class="relative p-1 text-gray-400 bg-red-400 rounded-full lg:hidden dark:bg-red-900 hover:text-white focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800 focus:outline-none" 142 + id="switchpageNotificationsTrigger" 143 + > 144 + <span class="absolute -inset-1.5"></span> 145 + <span class="sr-only">View notifications</span> 146 + <svg 147 + xmlns="http://www.w3.org/2000/svg" 148 + id="svg1" 149 + fill="none" 150 + stroke="currentColor" 151 + stroke-width="1.5" 152 + aria-hidden="true" 153 + class="inline w-6 h-6" 154 + viewBox="0 0 24 24" 155 + > 156 + <path 157 + id="path1" 158 + stroke-linecap="round" 159 + stroke-linejoin="round" 160 + d="M14.857 17.082a23.848 23.848 0 005.454-1.31A8.967 8.967 0 0118 9.75v-.7V9A6 6 0 006 9v.75a8.967 8.967 0 01-2.312 6.022c1.733.64 3.56 1.085 5.455 1.31m5.714 0a24.255 24.255 0 01-5.714 0m5.714 0a3 3 0 11-5.714 0" 161 + /> 162 + <ellipse 163 + style=" 164 + stroke-opacity: 1; 165 + stroke-dasharray: none; 166 + stroke-width: 0.9; 167 + stroke: #000; 168 + fill-rule: evenodd; 169 + fill: red; 170 + " 171 + id="path2" 172 + cx="16.526" 173 + cy="19.078" 174 + class="svg_activenotification" 175 + rx="1.919" 176 + ry="1.817" 177 + /> 178 + </svg> 179 + </button> 180 + 181 + <!-- Profile dropdown --> 182 + <div class="relative ml-3"> 183 + <div> 184 + <button 185 + type="button" 186 + class="relative flex text-sm bg-red-400 rounded-full dark:bg-red-900 focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800 focus:outline-none" 187 + id="user-menu-button" 188 + aria-expanded="false" 189 + aria-haspopup="true" 190 + > 191 + <span class="absolute -inset-1.5"></span> 192 + <span class="sr-only">Open user menu</span> 193 + <img 194 + class="w-8 h-8 rounded-full ownuseravatarsrc" 195 + src="" 196 + id="userimg" 197 + alt="" 198 + /> 199 + </button> 200 + </div> 201 + <div 202 + class="absolute right-0 z-10 w-48 py-1 mt-2 origin-top-right bg-white rounded-md shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none" 203 + role="menu" 204 + aria-orientation="vertical" 205 + aria-labelledby="user-menu-button" 206 + id="user-menu" 207 + tabindex="-1" 208 + > 209 + <div 210 + class="block px-4 py-2 text-sm text-gray-700" 211 + > 212 + Hi, 213 + <span 214 + class="p-1 text-sm italic bg-orange-700 rounded-md bg-opacity-20 settodisplayname" 215 + style=" 216 + overflow: hidden; 217 + white-space: nowrap; 218 + " 219 + >unset</span 220 + > 221 + </div> 222 + <hr /> 223 + <a 224 + href="javascript:void(0)" 225 + class="block px-4 py-2 text-sm text-gray-700" 226 + role="menuitem" 227 + tabindex="-1" 228 + id="user-menu-item-0" 229 + >User settings</a 230 + > 231 + <a 232 + href="javascript:void(0)" 233 + class="block px-4 py-2 text-sm text-gray-700" 234 + role="menuitem" 235 + tabindex="-1" 236 + id="user-menu-item-1" 237 + >Contact instance support</a 238 + > 239 + <a 240 + class="block px-4 py-2 text-sm text-gray-700 cursor-pointer logoutbutton" 241 + role="menuitem" 242 + tabindex="-1" 243 + id="user-menu-item-2" 244 + >Sign out</a 245 + > 246 + </div> 247 + </div> 248 + </div> 249 + </div> 250 + </div> 251 + <div class="z-50 sm:hidden" id="mobile-menu"> 252 + <div class="px-2 pt-2 pb-4 space-y-1"> 253 + <!-- Current: "bg-gray-900 text-white", Default: "text-gray-300 hover:bg-gray-700 hover:text-white" --> 254 + <a 255 + href="javascript:void(0)" 256 + class="" 257 + aria-current="page" 258 + id="mobile-home-nav" 259 + >Home</a 260 + > 261 + <a href="javascript:void(0)" class="" id="mobile-test-nav" 262 + >Tests for post rendering</a 263 + > 264 + <a 265 + href="javascript:void(0)" 266 + class="hidden" 267 + style="display: none" 268 + id="mobile-notifications-nav" 269 + ></a> 270 + </div> 271 + </div> 272 + </nav> 273 + <main 274 + class="monthclass-month dayclass-day w-screen h-screen text-fuchsia-900 lg:grid lg:grid-cols-5 lg:grid-flow-col dark:text-violet-200 not:max-h-[89VH] overflow-clip" 275 + > 276 + <div 277 + id="mainleft" 278 + class="hidden h-full lg:block bg-brown-200 overflow-clip dark:bg-neutral-600" 279 + ></div> 280 + <div 281 + class="h-full col-span-4 overflow-auto contentkeeper no:lg:p-12 bg-brown-100 dark:bg-neutral-700" 282 + > 283 + <div 284 + id="mainright" 285 + class="w-full p-4 rounded-md lg:p-8 lg:w-11/12 bg-brown-200 dark:bg-neutral-600 disabled:lg:h-10/12 lg:m-12" 286 + ></div> 287 + </div> 288 + <div 289 + class="fixed flex items-center justify-center w-12 h-12 p-0 text-sm bg-red-400 rounded-full shadow-2xl cursor-pointer bottom-3 lg:bottom-6 right-3 dark:bg-red-900 lg:w-20 lg:h-20 z-[70]" 290 + id="editorTrigger" 291 + > 292 + <svg 293 + width="120" 294 + height="120" 295 + version="1.1" 296 + viewBox="0 0 120 120" 297 + xml:space="preserve" 298 + xmlns="http://www.w3.org/2000/svg" 299 + > 300 + <g 301 + transform="matrix(3.0862 0 0 3.0862 24.842 20.186)" 302 + display="none" 303 + fill="none" 304 + stroke="#1c274c" 305 + stroke-width="1.5" 306 + > 307 + <path 308 + d="m15.287 3.1518-0.9268 0.92688-8.5213 8.5212h-1e-5c-0.57715 0.5772-0.86573 0.8657-1.1139 1.1839-0.29277 0.3754-0.54376 0.7815-0.74856 1.2112-0.17361 0.3643-0.30266 0.7515-0.56078 1.5258l-1.3611 4.0834c-0.12702 0.381-0.02785 0.8011 0.25618 1.0852 0.28403 0.284 0.70415 0.3832 1.0852 0.2562l0.80208-0.2674 3.2813-1.0938h1e-5c0.77434-0.2581 1.1615-0.3871 1.5258-0.5607 0.42971-0.2048 0.83584-0.4558 1.2112-0.7486 0.3182-0.2482 0.6067-0.5368 1.1839-1.1139l8.5212-8.5213 0.9269-0.92687c1.5357-1.5357 1.5357-4.0256 0-5.5613-1.5357-1.5357-4.0256-1.5357-5.5613 0z" 309 + /> 310 + <path 311 + d="m14.36 4.0781s0.1159 1.9696 1.8538 3.7075 3.7075 1.8538 3.7075 1.8538m-15.723 12.038-1.8761-1.8762" 312 + opacity=".5" 313 + /> 314 + </g> 315 + <g stroke-linecap="round" stroke-linejoin="round"> 316 + <ellipse 317 + cx="60.481" 318 + cy="59.218" 319 + rx="53.378" 320 + ry="53.18" 321 + fill-opacity=".098814" 322 + opacity=".854" 323 + stroke="currentColor" 324 + stroke-width="2.8898" 325 + /> 326 + <g stroke="currentColor"> 327 + <path 328 + transform="matrix(.18738 .032737 -.032737 .18738 32.779 64.063)" 329 + d="m23.999 84.377 8.5638-61.703 49.155 38.268z" 330 + stroke-width="2.7789" 331 + /> 332 + <path 333 + transform="matrix(.43357 .44112 -.79833 .78466 94.362 -13.574)" 334 + d="m24.951 75.234c-2.5778-3.0222-0.16522-33.42 2.857-35.998 3.0222-2.5778 33.42-0.16522 35.998 2.857 2.5778 3.0222 0.16522 33.42-2.857 35.998s-33.42 0.16522-35.998-2.857z" 335 + stroke-width="2.7789" 336 + /> 337 + <path 338 + d="m44.869 57.785-6.2376 12.805" 339 + stroke-width="2.2881" 340 + /> 341 + <path 342 + d="m44.009 77.824 13.937-2.9366" 343 + stroke-width="2.2881" 344 + /> 345 + </g> 346 + </g> 347 + <ellipse 348 + transform="rotate(-33.998)" 349 + cx="46.958" 350 + cy="77.102" 351 + rx="5.2378" 352 + ry="10.761" 353 + opacity=".85" 354 + stroke="currentColor" 355 + stroke-linecap="round" 356 + stroke-linejoin="round" 357 + stroke-width="2.7789" 358 + /> 359 + </svg> 360 + </div> 361 + </main> 362 + <div 363 + id="mobiletimelineswitcher" 364 + class="fixed inset-y-0 z-10 block w-12 h-12 p-1 mt-auto mb-auto text-sm bg-red-400 rounded-full left-1 lg:hidden dark:bg-red-900 focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800 focus:outline-none" 365 + > 366 + <!-- Shows the switcher allowing timeline travel on mobile. --> 367 + <button> 368 + <svg 369 + class="w-10 h-10" 370 + id="arrowright" 371 + width="50" 372 + height="50" 373 + fill="none" 374 + stroke="none" 375 + stroke-linecap="square" 376 + stroke-miterlimit="10" 377 + viewBox="0 0 50 50" 378 + xmlns="http://www.w3.org/2000/svg" 379 + > 380 + <defs id="defs20"> 381 + <clipPath> 382 + <path 383 + d="M 0,0 H 960 V 720 H 0 Z" 384 + clip-rule="nonzero" 385 + /> 386 + </clipPath> 387 + <clipPath id="p.0-1-4"> 388 + <path 389 + id="path1-2-0" 390 + d="M 0,0 H 960 V 720 H 0 Z" 391 + clip-rule="nonzero" 392 + /> 393 + </clipPath> 394 + </defs> 395 + <clipPath id="p.0"> 396 + <path 397 + id="path1" 398 + d="M 0,0 H 960 V 720 H 0 Z" 399 + clip-rule="nonzero" 400 + /> 401 + </clipPath> 402 + <path 403 + style=" 404 + fill: currentColor; 405 + fill-opacity: 1; 406 + stroke: currentColor; 407 + stroke-width: 2.848; 408 + stroke-linecap: round; 409 + stroke-linejoin: round; 410 + stroke-dasharray: none; 411 + paint-order: fill markers stroke; 412 + " 413 + d="m 30.152807,9.1863138 a 7.7145349,0.13753011 41.944235 0 0 5.615234,5.2304682 7.7145349,0.13753011 41.944235 0 0 5.859375,5.082031 7.7145349,0.13753011 41.944235 0 0 -5.613281,-5.230468 7.7145349,0.13753011 41.944235 0 0 -5.861328,-5.0820312 z M 45.035619,22.438267 a 0.42024437,0.94349396 0 0 0 -0.419922,0.943359 0.42024437,0.94349396 0 0 0 0.419922,0.945313 0.42024437,0.94349396 0 0 0 0.419922,-0.945313 0.42024437,0.94349396 0 0 0 -0.419922,-0.943359 z M 18.87351,23.049595 A 14.878937,0.26167485 0 0 0 3.9946033,23.311313 14.878937,0.26167485 0 0 0 18.87351,23.573032 14.878937,0.26167485 0 0 0 33.752416,23.311313 14.878937,0.26167485 0 0 0 18.87351,23.049595 Z m 22.796875,4.263672 a 0.13753011,7.7145349 48.055765 0 0 -5.859375,5.082031 0.13753011,7.7145349 48.055765 0 0 -5.615234,5.230469 0.13753011,7.7145349 48.055765 0 0 5.859374,-5.082032 0.13753011,7.7145349 48.055765 0 0 5.615235,-5.230468 z" 414 + /> 415 + </svg> 416 + </button> 417 + </div> 418 + <!-- Added an inline style to this element because for some reason it doesnt work otherwise, I'll figure that out later. --> 419 + <div 420 + class="fixed w-screen md:w-[85VH] lg:w-[70VH] h-[70VH] top-[15VH] bottom-[15VH] right-0 left-0 lg:right-[calc(50VW-30VH)] lg:left-[calc(50VW-30VH)] z-[60] resize backdrop-blur-sm" 421 + id="posteditor" 422 + style="backdrop-filter: blur(8px)" 423 + > 424 + <p 425 + class="w-full h-full text-black bg-white dark:text-white dark:bg-black" 426 + > 427 + Failed to load post editor. 428 + </p> 429 + </div> 430 + </body> 431 + </html>
+139
backend-rs/frontend_assets/html/index.html
··· 1 + <!-- 2 + ~ Copyright (c) 2024, MLC 'Strawmelonjuice' Bloeiman 3 + ~ 4 + ~ Licensed under the BSD 3-Clause License. See the LICENSE file for more info. 5 + --> 6 + <!--suppress HtmlUnknownTarget --> 7 + <!doctype html> 8 + <html lang="en"> 9 + <head> 10 + <title>Welcome - Lumina(@{{iid}})</title> 11 + <meta charset="UTF-8" /> 12 + <style></style> 13 + <script src="/app.js" type="module"></script> 14 + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 15 + </head> 16 + 17 + <body class="bg-brown-100 dark:bg-neutral-500"> 18 + <nav class="bg-emerald-200 dark:bg-teal-800"> 19 + <div class="px-2 mx-auto max-w-7xl sm:px-6 lg:px-8"> 20 + <div class="flex relative justify-between items-center h-16"> 21 + <div 22 + class="flex absolute inset-y-0 left-0 items-center sm:hidden" 23 + > 24 + <!-- Mobile menu button--> 25 + <button 26 + type="button" 27 + class="inline-flex relative justify-center items-center p-2 text-gray-400 rounded-md hover:text-white hover:bg-gray-700 focus:ring-2 focus:ring-inset focus:ring-white focus:outline-none" 28 + aria-controls="mobile-menu" 29 + aria-expanded="false" 30 + id="btn-mobile-menu" 31 + > 32 + <span class="absolute -inset-0.5"></span> 33 + <span class="sr-only">Open main menu</span> 34 + <svg 35 + class="block w-6 h-6" 36 + fill="none" 37 + viewBox="0 0 24 24" 38 + stroke-width="1.5" 39 + stroke="currentColor" 40 + aria-hidden="true" 41 + id="btn-mobile-menu-open" 42 + > 43 + <path 44 + stroke-linecap="round" 45 + stroke-linejoin="round" 46 + d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" 47 + /> 48 + </svg> 49 + <svg 50 + class="w-6 h-6" 51 + fill="none" 52 + viewBox="0 0 24 24" 53 + stroke-width="1.5" 54 + stroke="currentColor" 55 + aria-hidden="true" 56 + id="btn-mobile-menu-close" 57 + > 58 + <path 59 + stroke-linecap="round" 60 + stroke-linejoin="round" 61 + d="M6 18L18 6M6 6l12 12" 62 + /> 63 + </svg> 64 + </button> 65 + </div> 66 + <div 67 + class="flex flex-1 justify-center items-center sm:justify-start sm:items-stretch" 68 + > 69 + <div class="flex flex-shrink-0 items-center"> 70 + <img 71 + class="w-auto h-10 bg-opacity-60 rounded-md border-amber-600 bg-stone-100 dark:bg-stone-100" 72 + src="/logo.svg" 73 + alt="Lumina Instance" 74 + /> 75 + </div> 76 + </div> 77 + <div 78 + class="flex absolute inset-y-0 right-0 items-center pr-2 sm:static sm:inset-auto sm:pr-0 sm:ml-6" 79 + > 80 + <div class="hidden sm:block sm:ml-6"> 81 + <div class="flex space-x-4"> 82 + <!-- Current: "bg-gray-900 text-white", Default: "text-gray-300 hover:bg-gray-700 hover:text-white" --> 83 + <a 84 + href="/signup" 85 + class="py-2 px-3 text-sm font-medium text-white bg-gray-900 rounded-md border-2" 86 + aria-current="page" 87 + id="signup-nav" 88 + >Sign-up</a 89 + > 90 + <a 91 + href="/home" 92 + class="py-2 px-3 text-sm font-medium bg-orange-200 rounded-md border-2 border-emerald-600 dark:text-orange-200 dark:bg-yellow-700 hover:text-white hover:bg-gray-700 text-brown-800 dark:border-zinc-400" 93 + id="login-nav" 94 + >Log in</a 95 + > 96 + </div> 97 + </div> 98 + </div> 99 + <div 100 + class="flex absolute inset-y-0 right-0 items-center pr-2 sm:static sm:inset-auto sm:pr-0 sm:ml-6" 101 + ></div> 102 + </div> 103 + </div> 104 + <div class="sm:hidden" id="mobile-menu"> 105 + <div class="px-2 pt-2 pb-3 space-y-1"> 106 + <a 107 + href="/home" 108 + class="block py-2 px-3 text-base font-medium text-white bg-red-400 rounded-md dark:bg-red-900" 109 + id="mobile-login-nav" 110 + >Log in</a 111 + > 112 + <a 113 + href="/signup" 114 + class="block py-2 px-3 text-base font-medium text-gray-300 rounded-md hover:text-white hover:bg-gray-700" 115 + id="mobile-signup-nav" 116 + >Sign up</a 117 + > 118 + </div> 119 + </div> 120 + </nav> 121 + <main 122 + class="monthclass-month dayclass-day text-fuchsia-900 dark:text-violet-200" 123 + > 124 + <div class="contentkeeper"> 125 + <h1>Lumina</h1> 126 + <p> 127 + Hello, this is an Lumina instance you landed on! The 128 + specific ID of this instance is 129 + <code 130 + class="m-1 text-stone-500 bg-slate-200 dark:text-stone-200 dark:bg-slate-600" 131 + >localhost</code 132 + >. 133 + </p> 134 + <h2>What is Lumina?</h2> 135 + <p>haha, something that is not ready for you to see!</p> 136 + </div> 137 + </main> 138 + </body> 139 + </html>
+195
backend-rs/frontend_assets/html/login.html
··· 1 + <!-- 2 + ~ Copyright (c) 2024, MLC 'Strawmelonjuice' Bloeiman 3 + ~ 4 + ~ Licensed under the BSD 3-Clause License. See the LICENSE file for more info. 5 + --> 6 + 7 + <!doctype html> 8 + <!--suppress HtmlUnknownTarget --> 9 + <html lang="en"> 10 + <head> 11 + <title>Login - Lumina(@{{iid}})</title> 12 + <style></style> 13 + <link href="/logo.png" rel="icon" type="image/png" /> 14 + <script src="/app.js" type="module"></script> 15 + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 16 + </head> 17 + 18 + <body class="bg-brown-100 dark:bg-neutral-500"> 19 + <nav class="bg-emerald-200 dark:bg-teal-800"> 20 + <div class="px-2 mx-auto max-w-7xl sm:px-6 lg:px-8"> 21 + <div class="flex relative justify-between items-center h-16"> 22 + <div 23 + class="flex absolute inset-y-0 left-0 items-center sm:hidden" 24 + > 25 + <!-- Mobile menu button--> 26 + <button 27 + type="button" 28 + class="inline-flex relative justify-center items-center p-2 text-gray-400 rounded-md hover:text-white hover:bg-gray-700 focus:ring-2 focus:ring-inset focus:ring-white focus:outline-none" 29 + aria-controls="mobile-menu" 30 + aria-expanded="false" 31 + id="btn-mobile-menu" 32 + > 33 + <span class="absolute -inset-0.5"></span> 34 + <span class="sr-only">Open main menu</span> 35 + <svg 36 + class="block w-6 h-6" 37 + fill="none" 38 + viewBox="0 0 24 24" 39 + stroke-width="1.5" 40 + stroke="currentColor" 41 + aria-hidden="true" 42 + id="btn-mobile-menu-open" 43 + > 44 + <path 45 + stroke-linecap="round" 46 + stroke-linejoin="round" 47 + d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" 48 + /> 49 + </svg> 50 + <svg 51 + class="w-6 h-6" 52 + fill="none" 53 + viewBox="0 0 24 24" 54 + stroke-width="1.5" 55 + stroke="currentColor" 56 + aria-hidden="true" 57 + id="btn-mobile-menu-close" 58 + > 59 + <path 60 + stroke-linecap="round" 61 + stroke-linejoin="round" 62 + d="M6 18L18 6M6 6l12 12" 63 + /> 64 + </svg> 65 + </button> 66 + </div> 67 + <div 68 + class="flex flex-1 justify-center items-center sm:justify-start sm:items-stretch" 69 + > 70 + <div class="flex flex-shrink-0 items-center"> 71 + <img 72 + class="w-auto h-10 bg-opacity-60 rounded-md border-amber-600 bg-stone-100 dark:bg-stone-100" 73 + src="/logo.svg" 74 + alt="Lumina Instance" 75 + /> 76 + </div> 77 + </div> 78 + <div 79 + class="flex absolute inset-y-0 right-0 items-center pr-2 sm:static sm:inset-auto sm:pr-0 sm:ml-6" 80 + > 81 + <div class="hidden sm:block sm:ml-6"> 82 + <div class="flex space-x-4"> 83 + <!-- Current: "bg-gray-900 text-white", Default: "text-gray-300 hover:bg-gray-700 hover:text-white" --> 84 + <a 85 + href="/signup" 86 + class="py-2 px-3 text-sm font-medium bg-orange-200 rounded-md border-2 border-emerald-600 dark:text-orange-200 dark:bg-yellow-700 hover:text-white hover:bg-gray-700 text-brown-800 dark:border-zinc-400" 87 + id="signup-nav" 88 + >Sign-up</a 89 + > 90 + <a 91 + href="/home" 92 + class="py-2 px-3 text-sm font-medium text-white bg-gray-900 rounded-md border-2" 93 + id="login-nav" 94 + aria-current="page" 95 + >Log in</a 96 + > 97 + </div> 98 + </div> 99 + </div> 100 + <div 101 + class="flex absolute inset-y-0 right-0 items-center pr-2 sm:static sm:inset-auto sm:pr-0 sm:ml-6" 102 + ></div> 103 + </div> 104 + </div> 105 + <div class="sm:hidden" id="mobile-menu"> 106 + <div class="px-2 pt-2 pb-3 space-y-1"> 107 + <a 108 + href="/home" 109 + class="bg-red-400 dark:bg-red-900 text-white block rounded-md px-3 py-2 text-base font-medium" 110 + id="mobile-login-nav" 111 + >Log in</a 112 + > 113 + <a 114 + href="/signup" 115 + class="block rounded-md px-3 py-2 text-base font-medium bg-orange-200 text-brown-800 dark:text-orange-200 border-emerald-600 dark:bg-yellow-700 dark:border-zinc-400 hover:bg-gray-700 hover:text-white" 116 + id="mobile-signup-nav" 117 + >Sign up</a 118 + > 119 + </div> 120 + </div> 121 + </nav> 122 + <div 123 + class="grid place-items-center h-screen leading-snug ring-offset-rose-950 text-slate-600 monthclass-month dayclass-day" 124 + > 125 + <form 126 + action="javascript:void(0);" 127 + class="self-center w-5/12 ring-offset-rose-950 text-slate-600" 128 + name="login" 129 + id="loginform" 130 + > 131 + <div class="mb-4"> 132 + <label 133 + class="block mb-2 text-sm font-semibold text-slate-600 dark:text-slate-200" 134 + for="username" 135 + >Username or email</label 136 + > 137 + <input 138 + class="py-2 px-4 w-full rounded-lg border focus:ring-blue-500 form-input text-slate-600" 139 + id="username" 140 + required 141 + type="text" 142 + /> 143 + </div> 144 + <div class="mb-6"> 145 + <label 146 + class="block mb-2 text-sm font-semibold text-slate-600 dark:text-slate-200" 147 + for="password" 148 + >Password 149 + <input 150 + class="py-2 px-4 w-full rounded-lg border focus:ring-blue-500 form-input text-slate-600" 151 + id="password" 152 + placeholder="••••••••••••" 153 + required 154 + type="password" 155 + /> 156 + <span 157 + class="mt-1 text-xs text-slate-600 dark:text-slate-200" 158 + > 159 + Forgot password? There's no way we can help you for 160 + now. 161 + </span> 162 + </label> 163 + <div class="form-group"> 164 + <label for="autologin"> 165 + <input id="autologin" type="checkbox" /> 166 + Stay logged in<br /> 167 + <small style="font-size: 8px" 168 + >Let cookies do the job for you</small 169 + > 170 + </label> 171 + </div> 172 + </div> 173 + <button 174 + class="grid content-center place-items-center p-4 py-2 px-4 w-full bg-orange-200 rounded-lg border-amber-600 border-opacity-100 ring-rose-300 dark:text-orange-200 dark:bg-yellow-700 focus:ring-2 focus:ring-rose-500 focus:ring-opacity-50 focus:outline-none text-brown-800 dark:hover:bg-sky-900 hover:bg-sky-600" 175 + id="submitbutton" 176 + type="submit" 177 + > 178 + Authorize 179 + </button> 180 + <p 181 + class="mt-4 text-xs text-center text-slate-600 dark:text-slate-200" 182 + > 183 + <span id="Aaa1" 184 + >Do you not have an account on this instance yet? 185 + </span> 186 + <a 187 + class="text-blue-500 hover:underline refertohomesite" 188 + href="/signup" 189 + >Sign up</a 190 + >. 191 + </p> 192 + </form> 193 + </div> 194 + </body> 195 + </html>
+210
backend-rs/frontend_assets/html/signup.html
··· 1 + <!-- 2 + ~ Copyright (c) 2024, MLC 'Strawmelonjuice' Bloeiman 3 + ~ 4 + ~ Licensed under the BSD 3-Clause License. See the LICENSE file for more info. 5 + --> 6 + 7 + <!doctype html> 8 + <!--suppress HtmlUnknownTarget --> 9 + <html lang="en"> 10 + <head> 11 + <title>Sign up - Lumina(@{{iid}})</title> 12 + <style></style> 13 + <link href="/logo.png" rel="icon" type="image/png" /> 14 + <script src="/app.js" type="module"></script> 15 + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 16 + </head> 17 + 18 + <body class="bg-brown-100 dark:bg-neutral-500"> 19 + <nav class="bg-emerald-200 dark:bg-teal-800"> 20 + <div class="px-2 mx-auto max-w-7xl sm:px-6 lg:px-8"> 21 + <div class="flex relative justify-between items-center h-16"> 22 + <div 23 + class="flex absolute inset-y-0 left-0 items-center sm:hidden" 24 + > 25 + <!-- Mobile menu button--> 26 + <button 27 + type="button" 28 + class="inline-flex relative justify-center items-center p-2 text-gray-400 rounded-md hover:text-white hover:bg-gray-700 focus:ring-2 focus:ring-inset focus:ring-white focus:outline-none" 29 + aria-controls="mobile-menu" 30 + aria-expanded="false" 31 + id="btn-mobile-menu" 32 + > 33 + <span class="absolute -inset-0.5"></span> 34 + <span class="sr-only">Open main menu</span> 35 + <svg 36 + class="block w-6 h-6" 37 + fill="none" 38 + viewBox="0 0 24 24" 39 + stroke-width="1.5" 40 + stroke="currentColor" 41 + aria-hidden="true" 42 + id="btn-mobile-menu-open" 43 + > 44 + <path 45 + stroke-linecap="round" 46 + stroke-linejoin="round" 47 + d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" 48 + /> 49 + </svg> 50 + <svg 51 + class="w-6 h-6" 52 + fill="none" 53 + viewBox="0 0 24 24" 54 + stroke-width="1.5" 55 + stroke="currentColor" 56 + aria-hidden="true" 57 + id="btn-mobile-menu-close" 58 + > 59 + <path 60 + stroke-linecap="round" 61 + stroke-linejoin="round" 62 + d="M6 18L18 6M6 6l12 12" 63 + /> 64 + </svg> 65 + </button> 66 + </div> 67 + <div 68 + class="flex flex-1 justify-center items-center sm:justify-start sm:items-stretch" 69 + > 70 + <div class="flex flex-shrink-0 items-center"> 71 + <img 72 + class="w-auto h-10 bg-opacity-60 rounded-md border-amber-600 bg-stone-100 dark:bg-stone-100" 73 + src="/logo.svg" 74 + alt="Lumina Instance" 75 + /> 76 + </div> 77 + </div> 78 + <div 79 + class="flex absolute inset-y-0 right-0 items-center pr-2 sm:static sm:inset-auto sm:pr-0 sm:ml-6" 80 + > 81 + <div class="hidden sm:block sm:ml-6"> 82 + <div class="flex space-x-4"> 83 + <!-- Current: "bg-gray-900 text-white", Default: "text-gray-300 hover:bg-gray-700 hover:text-white" --> 84 + <a 85 + href="/signup" 86 + class="py-2 px-3 text-sm font-medium text-white bg-gray-900 rounded-md border-2" 87 + aria-current="page" 88 + id="signup-nav" 89 + >Sign-up</a 90 + > 91 + <a 92 + href="/home" 93 + class="py-2 px-3 text-sm font-medium bg-orange-200 rounded-md border-2 border-emerald-600 dark:text-orange-200 dark:bg-yellow-700 hover:text-white hover:bg-gray-700 text-brown-800 dark:border-zinc-400" 94 + id="login-nav" 95 + >Log in</a 96 + > 97 + </div> 98 + </div> 99 + </div> 100 + <div 101 + class="flex absolute inset-y-0 right-0 items-center pr-2 sm:static sm:inset-auto sm:pr-0 sm:ml-6" 102 + ></div> 103 + </div> 104 + </div> 105 + <div class="sm:hidden" id="mobile-menu"> 106 + <div class="px-2 pt-2 pb-3 space-y-1"> 107 + <a 108 + href="/home" 109 + class="block rounded-md px-3 py-2 text-base font-medium bg-orange-200 text-brown-800 dark:text-orange-200 border-emerald-600 dark:bg-yellow-700 dark:border-zinc-400 hover:bg-gray-700 hover:text-white" 110 + id="mobile-login-nav" 111 + >Log in</a 112 + > 113 + <a 114 + href="/signup" 115 + class="bg-red-400 dark:bg-red-900 text-white block rounded-md px-3 py-2 text-base font-medium" 116 + id="mobile-signup-nav" 117 + >Sign up</a 118 + > 119 + </div> 120 + </div> 121 + </nav> 122 + <div 123 + class="grid place-items-center h-screen leading-snug ring-offset-rose-950 text-slate-600 monthclass-month dayclass-day" 124 + > 125 + <form 126 + action="javascript:void(0);" 127 + class="self-center w-5/12 ring-offset-rose-950 text-slate-600" 128 + name="signup" 129 + id="registrationform" 130 + > 131 + <div class="mb-4"> 132 + <label 133 + id="usernameLabel" 134 + class="block mb-2 text-sm font-semibold text-slate-600 dark:text-slate-200" 135 + for="username" 136 + >Username</label 137 + > 138 + <input 139 + class="py-2 px-4 w-full rounded-lg border focus:ring-blue-500 form-input text-slate-600" 140 + id="username" 141 + placeholder="strawberrygrapes233" 142 + required 143 + type="text" 144 + /> 145 + </div> 146 + <div class="mb-4"> 147 + <label 148 + class="block mb-2 text-sm font-semibold text-slate-600 dark:text-slate-200" 149 + for="email" 150 + >Email</label 151 + > 152 + <input 153 + class="py-2 px-4 w-full rounded-lg border focus:ring-blue-500 form-input text-slate-600" 154 + id="email" 155 + placeholder="strawberrygrapes233@example.com" 156 + required 157 + type="email" 158 + /> 159 + <small style="font-size: 8px" 160 + >This email may be the only way to get back in to your 161 + account if you're locked out!</small 162 + > 163 + </div> 164 + <div class="mb-6"> 165 + <label 166 + class="block mb-2 text-sm font-semibold text-slate-600 dark:text-slate-200" 167 + for="password" 168 + >Password</label 169 + > 170 + <input 171 + class="py-2 px-4 w-full rounded-lg border focus:ring-blue-500 form-input text-slate-600" 172 + id="password" 173 + placeholder="••••••••••••" 174 + required 175 + type="password" 176 + /> 177 + <label 178 + class="block mb-2 text-sm font-semibold text-slate-600 dark:text-slate-200" 179 + for="password2" 180 + >Repeat password</label 181 + > 182 + <input 183 + class="py-2 px-4 w-full rounded-lg border focus:ring-blue-500 form-input text-slate-600" 184 + id="password2" 185 + placeholder="••••••••••••" 186 + required 187 + type="password" 188 + /> 189 + </div> 190 + <button 191 + class="grid content-center place-items-center p-4 py-2 px-4 w-full bg-orange-200 rounded-lg border-amber-600 border-opacity-100 ring-rose-300 dark:text-orange-200 dark:bg-yellow-700 focus:ring-2 focus:ring-rose-500 focus:ring-opacity-50 focus:outline-none text-brown-800 dark:hover:bg-sky-900 hover:bg-sky-600" 192 + id="submitbutton" 193 + type="submit" 194 + > 195 + Register 196 + </button> 197 + <p 198 + class="mt-4 text-xs text-center text-slate-600 dark:text-slate-200" 199 + > 200 + <span id="Aaa1">Do you already have an account? </span> 201 + <a 202 + class="text-blue-500 hover:underline refertohomesite" 203 + href="/login" 204 + >Log in</a 205 + >. 206 + </p> 207 + </form> 208 + </div> 209 + </body> 210 + </html>
+142
backend-rs/frontend_assets/html/writer.html
··· 1 + <!-- 2 + ~ Copyright (c) 2024, MLC 'Strawmelonjuice' Bloeiman 3 + ~ 4 + ~ Licensed under the BSD 3-Clause License. See the LICENSE file for more info. 5 + --> 6 + <!doctype html> 7 + <div 8 + id="editorwindow" 9 + class="relative w-full h-full border-2 rounded-lg bg-emerald-200 bg-opacity-60 dark:bg-teal-800 overflow-clip border-fuchsia-900 dark:border-violet-200" 10 + > 11 + <div 12 + id="editorwindowh" 13 + class="absolute top-0 w-full h-12 bg-teal-800 dark:bg-emerald-200 cursor-move md:h-[2cm]" 14 + > 15 + <header 16 + class="absolute top-0 flex items-center justify-center w-full text-orange-100 special h-1/2 dark:text-neutral-800" 17 + > 18 + Post editor 19 + </header> 20 + <nav 21 + id="editormodepicker" 22 + class="absolute h-1/2 bottom-0 grid grid-flow-col justify-stretch w-[calc(90%-2.25rem)] lg:w-[calc(90%-1cm)] pl-1" 23 + > 24 + <div 25 + class="editor-switcher flex items-center justify-center p-0 bg-orange-100 border-2 border-b-0 rounded-md rounded-b-none cursor-default border-emerald-600 dark:text-orange-100 dark:bg-neutral-800 text-brown-800 dark:border-zinc-400" 26 + data-mode-opener="short" 27 + > 28 + Text post mode 29 + </div> 30 + <div 31 + class="editor-switcher flex items-center justify-center p-0 border-2 rounded-md cursor-pointer bg-emerald-200 dark:bg-teal-800 border-emerald-600 dark:text-orange-100 hover:text-white hover:bg-gray-700 text-brown-800 dark:border-zinc-400" 32 + data-mode-opener="long" 33 + > 34 + Article mode 35 + </div> 36 + <div 37 + class="editor-switcher flex items-center justify-center p-0 border-2 rounded-md cursor-pointer bg-emerald-200 dark:bg-teal-800 border-emerald-600 dark:text-orange-100 hover:text-white hover:bg-gray-700 text-brown-800 dark:border-zinc-400" 38 + data-mode-opener="embed" 39 + > 40 + Embed mode 41 + </div> 42 + </nav> 43 + <div 44 + class="absolute top-0.5 right-0.5 w-9 h-9 lg:top-1 lg:right-1 lg:h-[1cm] lg:w-[1cm] flex justify-center items-center" 45 + > 46 + <button 47 + class="flex items-center justify-center w-full h-full text-3xl text-center text-white bg-red-600 rounded-full hover:text-black hover:bg-red-400" 48 + id="bttn_closeeditor" 49 + > 50 + &times; 51 + </button> 52 + </div> 53 + </div> 54 + <div 55 + id="editorwindowm" 56 + class="top-12 h-[calc(100%-2.5rem)] md:top-[2cm] absolute w-full md:h-[calc(100%-2cm)] bg-orange-100 dark:bg-neutral-800 dark:text-orange-100 text-brown-800" 57 + > 58 + <div data-mode-field="short" class="block h-[inherit] w-[inherit]"> 59 + <label for="editor-short-input">Mode: Post</label> 60 + <div 61 + tabindex="1" 62 + title="Enter text here!" 63 + id="editor-short-container" 64 + class="w-11/12 ml-auto mr-auto text-black lg:w-9/12 min-h-10 h-fit editor-container bg-slate-50 dark:bg-slate-600 dark:text-slate-100 outline-cyan-50 focus-within:outline-cyan-100 outline-2" 65 + > 66 + <div 67 + id="editor-short-preview" 68 + class="w-full h-full p-0 overflow-auto leading-10 text-center whitespace-pre-wrap" 69 + ></div> 70 + <input 71 + type="text" 72 + id="editor-short-input" 73 + class="hidden w-full h-full p-1 overflow-auto overflow-x-auto leading-3 resize-none font-inherit text-black" 74 + value="Enter markdown like *italic* or **bold**" 75 + /> 76 + </div> 77 + <details> 78 + <summary> 79 + The post editor supports <em>simple MarkDown</em> 80 + </summary> 81 + <blockquote 82 + class="p-0 [&>*]:pl-2 ml-3 mr-3 border-gray-300 border-s-4 bg-gray-50 dark:border-gray-500 dark:bg-gray-800" 83 + > 84 + <ul> 85 + <li>Use * or _ to make text italic</li> 86 + <li>Use ** to make text bold</li> 87 + <li>Use ` to use a code block</li> 88 + <!-- <li>Use [text](url) to make a link</li> --> 89 + </ul> 90 + <p>The article editor supports more advanced MarkDown.</p> 91 + </blockquote> 92 + </details> 93 + </div> 94 + <div data-mode-field="long" class="hidden h-[inherit] w-[inherit]"> 95 + <label for="editor-long-input">Mode: Article</label 96 + ><button 97 + class="absolute px-3 py-2 text-sm font-medium bg-orange-200 border-2 rounded-md top-1 right-1 border-emerald-600 dark:text-orange-200 dark:bg-yellow-700 hover:text-white hover:bg-gray-700 text-brown-800 dark:border-zinc-400 outline-cyan-50 focus-within:outline-cyan-100 outline-2" 98 + onclick="document.activeElement.blur()" 99 + > 100 + Preview 101 + </button> 102 + <div 103 + tabindex="1" 104 + title="Enter text here!" 105 + id="editor-long-container" 106 + class="w-11/12 h-full ml-auto mr-auto text-black leading-2 lg:w-9/12 editor-container bg-slate-50 dark:bg-slate-600 dark:text-slate-100" 107 + > 108 + <div 109 + id="editor-long-preview" 110 + class="w-full h-full p-1 overflow-auto whitespace-pre-wrap leading-2" 111 + ></div> 112 + <textarea 113 + name="" 114 + id="editor-long-input" 115 + cols="30" 116 + rows="10" 117 + class="hidden w-full h-full p-1 overflow-auto overflow-x-auto resize-none bg-inherit text-inherit font-inherit" 118 + placeholder="Enter markdown like _italic_ or *bold*!" 119 + ></textarea> 120 + </div> 121 + <p> 122 + The article editor supports 123 + <a 124 + href="https://github.github.com/gfm/" 125 + target="_blank" 126 + class="text-blue-300 dark:text-blue-200" 127 + >GitHub-flavoured MarkDown</a 128 + >. See the 129 + <a 130 + href="https://gist.github.com/roshith-balendran/d50b32f8f7d900c34a7dc00766bcfb9c" 131 + target="_blank" 132 + class="text-blue-300 dark:text-blue-200" 133 + >cheatsheet</a 134 + > 135 + for help. 136 + </p> 137 + </div> 138 + <div data-mode-field="embed" class="hidden h-[inherit]"> 139 + Mode: Embed 140 + </div> 141 + </div> 142 + </div>
backend-rs/frontend_assets/png/luminalogo-0.png

This is a binary file and will not be displayed.

backend-rs/frontend_assets/png/luminalogo-1.png

This is a binary file and will not be displayed.

+1
backend-rs/frontend_assets/styles/initial_customstyles.css
··· 1 + /* This CSS file is here to override theme choices and colorschemes. If not defined, defaults compiled into Lumina are used. */
+117
backend-rs/frontend_assets/styles/main.pcss
··· 1 + /*! 2 + * Copyright (c) 2024, MLC 'Strawmelonjuice' Bloeiman 3 + * 4 + * Licensed under the BSD 3-Clause License. See the LICENSE file for more info. 5 + */ 6 + 7 + @font-face { 8 + font-family: "Josefin Sans"; 9 + src: url(/fonts/Josefin_Sans/JosefinSans-VariableFont_wght.ttf); 10 + } 11 + 12 + @font-face { 13 + font-family: "Fira Sans"; 14 + src: url(/fonts/Fira_Sans/FiraSans-Regular.ttf); 15 + } 16 + 17 + @font-face { 18 + font-family: "Gantari"; 19 + src: url(/fonts/Gantari/Gantari-VariableFont_wght.ttf); 20 + } 21 + 22 + @font-face { 23 + font-family: "Syne"; 24 + src: url(/fonts/Syne/Syne-VariableFont_wght.ttf); 25 + } 26 + 27 + @tailwind base; 28 + @tailwind components; 29 + @tailwind utilities; 30 + 31 + @layer base { 32 + html { 33 + font-family: "Josefin Sans", "Fira Sans", serif; 34 + } 35 + 36 + h1, 37 + h2, 38 + h3, 39 + h4, 40 + h5, 41 + h6, 42 + .special { 43 + font-family: "Gantari", "Syne", cursive; 44 + } 45 + h1, 46 + h2, 47 + h3, 48 + h4, 49 + h5, 50 + h6 { 51 + font-size: revert; 52 + font-weight: revert; 53 + } 54 + } 55 + 56 + body { 57 + overscroll-behavior-y: contain; 58 + } 59 + 60 + /*noinspection ALL*/ 61 + #homegrid { 62 + display: grid; 63 + grid-auto-flow: column; 64 + grid-auto-columns: 20%; 65 + } 66 + 67 + .articlecontent > p { 68 + font-size: 1.125rem /* 18px */; 69 + line-height: 1.75rem /* 28px */; 70 + } 71 + 72 + .postbttn { 73 + position: relative; 74 + display: inline-block; 75 + border-bottom: 1px dotted black; /* If you want dots under the hoverable text */ 76 + } 77 + .postbttn .postbttndescription { 78 + visibility: hidden; 79 + width: 120px; 80 + pointer-events: none; 81 + text-align: center; 82 + padding: 5px 0; 83 + border-radius: 6px; 84 + position: absolute; 85 + z-index: 1; 86 + } 87 + 88 + .postbttn:hover .postbttndescription { 89 + visibility: visible; 90 + top: -5px; 91 + right: 105%; 92 + } 93 + @media (prefers-color-scheme: dark) { 94 + } 95 + .postbttn .postbttndescription::after { 96 + content: " "; 97 + position: absolute; 98 + bottom: 10%; 99 + left: 100%; 100 + margin-top: -5px; 101 + border-width: 5px; 102 + border-style: solid; 103 + border-color: transparent transparent transparent black; 104 + } 105 + @media (prefers-color-scheme: dark) { 106 + .postbttn .postbttndescription::after { 107 + border-color: transparent transparent transparent rgb(224 206 199); 108 + } 109 + } 110 + 111 + .editor-container:focus-within > div { 112 + display: none; 113 + } 114 + 115 + .editor-container:focus-within :is(#editor-short-input, #editor-long-input) { 116 + display: initial; 117 + }
+7
backend-rs/frontend_assets/svg/add.svg
··· 1 + <?xml version="1.0" encoding="UTF-8"?> 2 + <!-- 3 + ~ Copyright (c) 2024, MLC 'Strawmelonjuice' Bloeiman 4 + ~ 5 + ~ Licensed under the BSD 3-Clause License. See the LICENSE file for more info. 6 + --> 7 + <svg width="120" height="120" version="1.1" viewBox="0 0 120 120" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><g transform="matrix(3.0862 0 0 3.0862 24.842 20.186)" display="none" fill="none" stroke="#1c274c" stroke-width="1.5"><path d="m15.287 3.1518-0.9268 0.92688-8.5213 8.5212h-1e-5c-0.57715 0.5772-0.86573 0.8657-1.1139 1.1839-0.29277 0.3754-0.54376 0.7815-0.74856 1.2112-0.17361 0.3643-0.30266 0.7515-0.56078 1.5258l-1.3611 4.0834c-0.12702 0.381-0.02785 0.8011 0.25618 1.0852 0.28403 0.284 0.70415 0.3832 1.0852 0.2562l0.80208-0.2674 3.2813-1.0938h1e-5c0.77434-0.2581 1.1615-0.3871 1.5258-0.5607 0.42971-0.2048 0.83584-0.4558 1.2112-0.7486 0.3182-0.2482 0.6067-0.5368 1.1839-1.1139l8.5212-8.5213 0.9269-0.92687c1.5357-1.5357 1.5357-4.0256 0-5.5613-1.5357-1.5357-4.0256-1.5357-5.5613 0z"/><path d="m14.36 4.0781s0.1159 1.9696 1.8538 3.7075 3.7075 1.8538 3.7075 1.8538m-15.723 12.038-1.8761-1.8762" opacity=".5"/></g><g stroke-linecap="round" stroke-linejoin="round"><ellipse cx="60.481" cy="59.218" rx="53.378" ry="53.18" fill-opacity=".098814" opacity=".854" stroke="currentColor" stroke-width="2.8898"/><g stroke="currentColor"><path transform="matrix(.18738 .032737 -.032737 .18738 32.779 64.063)" d="m23.999 84.377 8.5638-61.703 49.155 38.268z" stroke-width="2.7789"/><path transform="matrix(.43357 .44112 -.79833 .78466 94.362 -13.574)" d="m24.951 75.234c-2.5778-3.0222-0.16522-33.42 2.857-35.998 3.0222-2.5778 33.42-0.16522 35.998 2.857 2.5778 3.0222 0.16522 33.42-2.857 35.998s-33.42 0.16522-35.998-2.857z" stroke-width="2.7789"/><path d="m44.869 57.785-6.2376 12.805" stroke-width="2.2881"/><path d="m44.009 77.824 13.937-2.9366" stroke-width="2.2881"/></g></g><ellipse transform="rotate(-33.998)" cx="46.958" cy="77.102" rx="5.2378" ry="10.761" opacity=".85" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.7789"/></svg>
+6
backend-rs/frontend_assets/svg/arrow-right.svg
··· 1 + <!-- 2 + ~ Copyright (c) 2024, MLC 'Strawmelonjuice' Bloeiman 3 + ~ 4 + ~ Licensed under the BSD 3-Clause License. See the LICENSE file for more info. 5 + --> 6 + <svg xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" id="svg20" width="50" height="50" fill="none" stroke="none" stroke-linecap="square" stroke-miterlimit="10" version="1.1" viewBox="0 0 50 50"><defs id="defs20"><clipPath id="p.0-1"><path id="path1-2" d="M 0,0 H 960 V 720 H 0 Z" clip-rule="nonzero"/></clipPath><clipPath id="p.0-1-4"><path id="path1-2-0" d="M 0,0 H 960 V 720 H 0 Z" clip-rule="nonzero"/></clipPath></defs><clipPath id="p.0"><path id="path1" d="M 0,0 H 960 V 720 H 0 Z" clip-rule="nonzero"/></clipPath><path id="arrowright" style="fill:currentColor;fill-opacity:1;stroke:currentColor;stroke-width:2.848;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;paint-order:fill markers stroke" d="m 30.152807,9.1863138 a 7.7145349,0.13753011 41.944235 0 0 5.615234,5.2304682 7.7145349,0.13753011 41.944235 0 0 5.859375,5.082031 7.7145349,0.13753011 41.944235 0 0 -5.613281,-5.230468 7.7145349,0.13753011 41.944235 0 0 -5.861328,-5.0820312 z M 45.035619,22.438267 a 0.42024437,0.94349396 0 0 0 -0.419922,0.943359 0.42024437,0.94349396 0 0 0 0.419922,0.945313 0.42024437,0.94349396 0 0 0 0.419922,-0.945313 0.42024437,0.94349396 0 0 0 -0.419922,-0.943359 z M 18.87351,23.049595 A 14.878937,0.26167485 0 0 0 3.9946033,23.311313 14.878937,0.26167485 0 0 0 18.87351,23.573032 14.878937,0.26167485 0 0 0 33.752416,23.311313 14.878937,0.26167485 0 0 0 18.87351,23.049595 Z m 22.796875,4.263672 a 0.13753011,7.7145349 48.055765 0 0 -5.859375,5.082031 0.13753011,7.7145349 48.055765 0 0 -5.615234,5.230469 0.13753011,7.7145349 48.055765 0 0 5.859374,-5.082032 0.13753011,7.7145349 48.055765 0 0 5.615235,-5.230468 z"/></svg>
+6
backend-rs/frontend_assets/svg/avatar1.svg
··· 1 + <!-- 2 + ~ Copyright (c) 2024, MLC 'Strawmelonjuice' Bloeiman 3 + ~ 4 + ~ Licensed under the BSD 3-Clause License. See the LICENSE file for more info. 5 + --> 6 + <svg xmlns="http://www.w3.org/2000/svg" id="svg20" width="50" height="50" fill="none" stroke="none" stroke-linecap="square" stroke-miterlimit="10" version="1.1" viewBox="0 0 50 50"><defs id="defs20"><clipPath id="p.0-1"><path id="path1-2" d="M 0,0 H 960 V 720 H 0 Z" clip-rule="nonzero"/></clipPath><clipPath id="p.0-1-4"><path id="path1-2-0" d="M 0,0 H 960 V 720 H 0 Z" clip-rule="nonzero"/></clipPath></defs><clipPath id="p.0"><path id="path1" d="M 0,0 H 960 V 720 H 0 Z" clip-rule="nonzero"/></clipPath><ellipse style="display:block;fill:#c4c4c4;fill-opacity:.348548;stroke:none;stroke-width:6.38572;stroke-dasharray:none;shape-rendering:auto" id="path2" cx="25.036" cy="25.14" rx="24.894" ry="24.993"/><path id="path3" fill="#000" fill-opacity="0" fill-rule="evenodd" d="M 13.675942,34.154429 3.3403878,63.076639" style="stroke-width:.104845"/><path id="path4" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="M 13.675942,34.154429 3.3403878,63.076639"/><path id="path5" fill="#000" fill-opacity="0" fill-rule="evenodd" d="M 3.3394916,63.076641 26.463401,62.627244" style="stroke-width:.104845"/><path id="path6" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="M 3.3394916,63.076641 26.463401,62.627244"/><path id="path7" fill="#000" fill-opacity="0" fill-rule="evenodd" d="M 37.137291,33.627544 49.257679,62.051776" style="stroke-width:.104845"/><path id="path8" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="M 37.137291,33.627544 49.257679,62.051776"/><path id="path9" fill="#000" fill-opacity="0" fill-rule="evenodd" d="M 49.257334,62.050424 26.137015,62.62735" style="stroke-width:.104845"/><path id="path10" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="M 49.257334,62.050424 26.137015,62.62735"/><path id="path11" fill="#cfe2f3" fill-rule="evenodd" d="m 9.6085775,26.677672 v 0 c 0,-7.605971 6.7859175,-13.771827 15.1567725,-13.771827 q 7.578387,0 15.156777,0 0,6.885913 0,13.771827 v 0 c 0,7.605972 -6.785918,13.771829 -15.156777,13.771829 v 0 c -8.370854,0 -15.1567725,-6.165857 -15.1567725,-13.771829 z" style="fill:#b9bbee;fill-opacity:1;stroke-width:.104845"/><path id="path12" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="m 9.6085775,26.677672 v 0 c 0,-7.605971 6.7859175,-13.771827 15.1567725,-13.771827 q 7.578387,0 15.156777,0 0,6.885913 0,13.771827 v 0 c 0,7.605972 -6.785918,13.771829 -15.156777,13.771829 v 0 c -8.370854,0 -15.1567725,-6.165857 -15.1567725,-13.771829 z"/><path id="path13" fill="#434343" fill-rule="evenodd" d="m 14.49534,21.551888 v 0 c 0,-1.267803 0.992846,-2.295558 2.217581,-2.295558 v 0 c 0.588139,0 1.152192,0.241852 1.568067,0.672353 0.415878,0.430502 0.649516,1.014385 0.649516,1.623205 v 0 c 0,1.267801 -0.992845,2.295557 -2.217583,2.295557 v 0 c -1.224735,0 -2.217581,-1.027756 -2.217581,-2.295557 z" style="stroke-width:.104845"/><path id="path14" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="m 14.49534,21.551888 v 0 c 0,-1.267803 0.992846,-2.295558 2.217581,-2.295558 v 0 c 0.588139,0 1.152192,0.241852 1.568067,0.672353 0.415878,0.430502 0.649516,1.014385 0.649516,1.623205 v 0 c 0,1.267801 -0.992845,2.295557 -2.217583,2.295557 v 0 c -1.224735,0 -2.217581,-1.027756 -2.217581,-2.295557 z"/><path id="path20" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="m 20.920344,32.493238 c 1.379928,0 2.069892,-0.210275 2.759856,-0.420549 0.689963,-0.210275 1.379927,-0.420548 2.759858,-0.420548"/><path id="path15" fill="#434343" fill-rule="evenodd" d="m 26.060287,21.551888 v 0 c 0,-1.267803 0.992843,-2.295558 2.217583,-2.295558 v 0 c 0.588137,0 1.152191,0.241852 1.568069,0.672353 0.415875,0.430502 0.649513,1.014385 0.649513,1.623205 v 0 c 0,1.267801 -0.992847,2.295557 -2.217582,2.295557 v 0 c -1.22474,0 -2.217583,-1.027756 -2.217583,-2.295557 z" style="stroke-width:.104845"/><path id="path16" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="m 26.060287,21.551888 v 0 c 0,-1.267803 0.992843,-2.295558 2.217583,-2.295558 v 0 c 0.588137,0 1.152191,0.241852 1.568069,0.672353 0.415875,0.430502 0.649513,1.014385 0.649513,1.623205 v 0 c 0,1.267801 -0.992847,2.295557 -2.217582,2.295557 v 0 c -1.22474,0 -2.217583,-1.027756 -2.217583,-2.295557 z"/><path id="path17" fill="#000" fill-opacity="0" fill-rule="evenodd" d="m 16.124261,30.350767 c 1.176125,0 1.764191,0.516197 2.352254,1.032392 0.588061,0.5162 1.176126,1.032396 2.352253,1.032396" style="stroke-width:.104845"/><path id="path18" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="m 16.124261,30.350767 c 1.176125,0 1.764191,0.516197 2.352254,1.032392 0.588061,0.5162 1.176126,1.032396 2.352253,1.032396"/><path id="path19" fill="#000" fill-opacity="0" fill-rule="evenodd" d="m 20.920344,32.493238 c 1.379928,0 2.069892,-0.210275 2.759856,-0.420549 0.689963,-0.210275 1.379927,-0.420548 2.759858,-0.420548" style="stroke-width:.104845"/><path style="fill:maroon;fill-opacity:.348548;stroke-width:.108022" id="path21" d="M 3.5645052,62.796866 C 3.6113167,62.628582 12.372818,38.099617 12.938598,36.543302 l 0.276237,-0.759135 0.848439,0.750487 c 1.871046,1.654958 3.780284,2.690927 6.22895,3.37969 1.610931,0.452965 2.540168,0.572693 4.478365,0.574688 1.354292,0.0016 1.848306,-0.03327 2.692999,-0.172274 3.687715,-0.621914 7.078461,-2.478145 9.331288,-5.108942 l 0.593271,-0.692819 0.105003,0.225485 c 0.554477,1.190483 11.566724,27.158366 11.529936,27.189496 -0.08494,0.07117 -21.916135,0.603955 -36.430422,0.88877 -9.9347255,0.19489 -9.0927079,0.196886 -9.0278297,-0.01995 z"/></svg>
+6
backend-rs/frontend_assets/svg/avatar2.svg
··· 1 + <!-- 2 + ~ Copyright (c) 2024, MLC 'Strawmelonjuice' Bloeiman 3 + ~ 4 + ~ Licensed under the BSD 3-Clause License. See the LICENSE file for more info. 5 + --> 6 + <svg xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" id="svg20" width="50" height="50" fill="none" stroke="none" stroke-linecap="square" stroke-miterlimit="10" version="1.1" viewBox="0 0 50 50"><defs id="defs20"><clipPath id="p.0-1"><path id="path1-2" d="M 0,0 H 960 V 720 H 0 Z" clip-rule="nonzero"/></clipPath><clipPath id="p.0-1-4"><path id="path1-2-0" d="M 0,0 H 960 V 720 H 0 Z" clip-rule="nonzero"/></clipPath></defs><clipPath id="p.0"><path id="path1" d="M 0,0 H 960 V 720 H 0 Z" clip-rule="nonzero"/></clipPath><ellipse style="display:block;fill:#c4c4c4;fill-opacity:.348548;stroke:none;stroke-width:6.38572;stroke-dasharray:none;shape-rendering:auto" id="path2" cx="25.036" cy="25.14" rx="24.894" ry="24.993"/><path id="path3" fill="#000" fill-opacity="0" fill-rule="evenodd" d="M 13.675942,34.154429 3.3403878,63.076639" style="stroke-width:.104845"/><path id="path4" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="M 13.675942,34.154429 3.3403878,63.076639"/><path id="path5" fill="#000" fill-opacity="0" fill-rule="evenodd" d="M 3.3394916,63.076641 26.463401,62.627244" style="stroke-width:.104845"/><path id="path6" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="M 3.3394916,63.076641 26.463401,62.627244"/><path id="path7" fill="#000" fill-opacity="0" fill-rule="evenodd" d="M 37.137291,33.627544 49.257679,62.051776" style="stroke-width:.104845"/><path id="path8" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="M 37.137291,33.627544 49.257679,62.051776"/><path id="path9" fill="#000" fill-opacity="0" fill-rule="evenodd" d="M 49.257334,62.050424 26.137015,62.62735" style="stroke-width:.104845"/><path id="path10" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="M 49.257334,62.050424 26.137015,62.62735"/><path id="path11" fill="#cfe2f3" fill-rule="evenodd" d="m 9.6085775,26.677672 v 0 c 0,-7.605971 6.7859175,-13.771827 15.1567725,-13.771827 q 7.578387,0 15.156777,0 0,6.885913 0,13.771827 v 0 c 0,7.605972 -6.785918,13.771829 -15.156777,13.771829 v 0 c -8.370854,0 -15.1567725,-6.165857 -15.1567725,-13.771829 z" style="fill:#eeddb9;fill-opacity:1;stroke-width:.104845"/><path id="path12" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="m 9.6085775,26.677672 v 0 c 0,-7.605971 6.7859175,-13.771827 15.1567725,-13.771827 q 7.578387,0 15.156777,0 0,6.885913 0,13.771827 v 0 c 0,7.605972 -6.785918,13.771829 -15.156777,13.771829 v 0 c -8.370854,0 -15.1567725,-6.165857 -15.1567725,-13.771829 z"/><path id="path13" fill="#434343" fill-rule="evenodd" d="m 14.49534,21.551888 v 0 c 0,-1.267803 0.992846,-2.295558 2.217581,-2.295558 v 0 c 0.588139,0 1.152192,0.241852 1.568067,0.672353 0.415878,0.430502 0.649516,1.014385 0.649516,1.623205 v 0 c 0,1.267801 -0.992845,2.295557 -2.217583,2.295557 v 0 c -1.224735,0 -2.217581,-1.027756 -2.217581,-2.295557 z" style="stroke-width:.104845"/><path id="path14" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="m 14.49534,21.551888 v 0 c 0,-1.267803 0.992846,-2.295558 2.217581,-2.295558 v 0 c 0.588139,0 1.152192,0.241852 1.568067,0.672353 0.415878,0.430502 0.649516,1.014385 0.649516,1.623205 v 0 c 0,1.267801 -0.992845,2.295557 -2.217583,2.295557 v 0 c -1.224735,0 -2.217581,-1.027756 -2.217581,-2.295557 z"/><path id="path20" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="m 20.920344,32.493238 c 1.379928,0 2.069892,-0.210275 2.759856,-0.420549 0.689963,-0.210275 1.379927,-0.420548 2.759858,-0.420548"/><path id="path15" fill="#434343" fill-rule="evenodd" d="m 26.060287,21.551888 v 0 c 0,-1.267803 0.992843,-2.295558 2.217583,-2.295558 v 0 c 0.588137,0 1.152191,0.241852 1.568069,0.672353 0.415875,0.430502 0.649513,1.014385 0.649513,1.623205 v 0 c 0,1.267801 -0.992847,2.295557 -2.217582,2.295557 v 0 c -1.22474,0 -2.217583,-1.027756 -2.217583,-2.295557 z" style="stroke-width:.104845"/><path id="path16" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="m 26.060287,21.551888 v 0 c 0,-1.267803 0.992843,-2.295558 2.217583,-2.295558 v 0 c 0.588137,0 1.152191,0.241852 1.568069,0.672353 0.415875,0.430502 0.649513,1.014385 0.649513,1.623205 v 0 c 0,1.267801 -0.992847,2.295557 -2.217582,2.295557 v 0 c -1.22474,0 -2.217583,-1.027756 -2.217583,-2.295557 z"/><path id="path17" fill="#000" fill-opacity="0" fill-rule="evenodd" d="m 16.124261,30.350767 c 1.176125,0 1.764191,0.516197 2.352254,1.032392 0.588061,0.5162 1.176126,1.032396 2.352253,1.032396" style="stroke-width:.104845"/><path id="path18" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="m 16.124261,30.350767 c 1.176125,0 1.764191,0.516197 2.352254,1.032392 0.588061,0.5162 1.176126,1.032396 2.352253,1.032396"/><path id="path19" fill="#000" fill-opacity="0" fill-rule="evenodd" d="m 20.920344,32.493238 c 1.379928,0 2.069892,-0.210275 2.759856,-0.420549 0.689963,-0.210275 1.379927,-0.420548 2.759858,-0.420548" style="stroke-width:.104845"/><path style="fill:#002780;fill-opacity:.34854802;stroke-width:.108022" id="path21" d="M 3.5645052,62.796866 C 3.6113167,62.628582 12.372818,38.099617 12.938598,36.543302 l 0.276237,-0.759135 0.848439,0.750487 c 1.871046,1.654958 3.780284,2.690927 6.22895,3.37969 1.610931,0.452965 2.540168,0.572693 4.478365,0.574688 1.354292,0.0016 1.848306,-0.03327 2.692999,-0.172274 3.687715,-0.621914 7.078461,-2.478145 9.331288,-5.108942 l 0.593271,-0.692819 0.105003,0.225485 c 0.554477,1.190483 11.566724,27.158366 11.529936,27.189496 -0.08494,0.07117 -21.916135,0.603955 -36.430422,0.88877 -9.9347255,0.19489 -9.0927079,0.196886 -9.0278297,-0.01995 z"/></svg>
+6
backend-rs/frontend_assets/svg/avatar3.svg
··· 1 + <!-- 2 + ~ Copyright (c) 2024, MLC 'Strawmelonjuice' Bloeiman 3 + ~ 4 + ~ Licensed under the BSD 3-Clause License. See the LICENSE file for more info. 5 + --> 6 + <svg xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" id="svg20" width="50" height="50" fill="none" stroke="none" stroke-linecap="square" stroke-miterlimit="10" version="1.1" viewBox="0 0 50 50"><defs id="defs20"><clipPath id="p.0-1"><path id="path1-2" d="M 0,0 H 960 V 720 H 0 Z" clip-rule="nonzero"/></clipPath><clipPath id="p.0-1-4"><path id="path1-2-0" d="M 0,0 H 960 V 720 H 0 Z" clip-rule="nonzero"/></clipPath></defs><clipPath id="p.0"><path id="path1" d="M 0,0 H 960 V 720 H 0 Z" clip-rule="nonzero"/></clipPath><ellipse style="display:block;fill:#c4c4c4;fill-opacity:.348548;stroke:none;stroke-width:6.38572;stroke-dasharray:none;shape-rendering:auto" id="path2" cx="25.036" cy="25.14" rx="24.894" ry="24.993"/><path id="path3" fill="#000" fill-opacity="0" fill-rule="evenodd" d="M 13.675942,34.154429 3.3403878,63.076639" style="stroke-width:.104845"/><path id="path4" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="M 13.675942,34.154429 3.3403878,63.076639"/><path id="path5" fill="#000" fill-opacity="0" fill-rule="evenodd" d="M 3.3394916,63.076641 26.463401,62.627244" style="stroke-width:.104845"/><path id="path6" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="M 3.3394916,63.076641 26.463401,62.627244"/><path id="path7" fill="#000" fill-opacity="0" fill-rule="evenodd" d="M 37.137291,33.627544 49.257679,62.051776" style="stroke-width:.104845"/><path id="path8" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="M 37.137291,33.627544 49.257679,62.051776"/><path id="path9" fill="#000" fill-opacity="0" fill-rule="evenodd" d="M 49.257334,62.050424 26.137015,62.62735" style="stroke-width:.104845"/><path id="path10" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="M 49.257334,62.050424 26.137015,62.62735"/><path id="path11" fill="#cfe2f3" fill-rule="evenodd" d="m 9.6085775,26.677672 v 0 c 0,-7.605971 6.7859175,-13.771827 15.1567725,-13.771827 q 7.578387,0 15.156777,0 0,6.885913 0,13.771827 v 0 c 0,7.605972 -6.785918,13.771829 -15.156777,13.771829 v 0 c -8.370854,0 -15.1567725,-6.165857 -15.1567725,-13.771829 z" style="fill:#a8ffa3;fill-opacity:1;stroke-width:.104845"/><path id="path12" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="m 9.6085775,26.677672 v 0 c 0,-7.605971 6.7859175,-13.771827 15.1567725,-13.771827 q 7.578387,0 15.156777,0 0,6.885913 0,13.771827 v 0 c 0,7.605972 -6.785918,13.771829 -15.156777,13.771829 v 0 c -8.370854,0 -15.1567725,-6.165857 -15.1567725,-13.771829 z"/><path id="path13" fill="#434343" fill-rule="evenodd" d="m 14.49534,21.551888 v 0 c 0,-1.267803 0.992846,-2.295558 2.217581,-2.295558 v 0 c 0.588139,0 1.152192,0.241852 1.568067,0.672353 0.415878,0.430502 0.649516,1.014385 0.649516,1.623205 v 0 c 0,1.267801 -0.992845,2.295557 -2.217583,2.295557 v 0 c -1.224735,0 -2.217581,-1.027756 -2.217581,-2.295557 z" style="stroke-width:.104845"/><path id="path14" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="m 14.49534,21.551888 v 0 c 0,-1.267803 0.992846,-2.295558 2.217581,-2.295558 v 0 c 0.588139,0 1.152192,0.241852 1.568067,0.672353 0.415878,0.430502 0.649516,1.014385 0.649516,1.623205 v 0 c 0,1.267801 -0.992845,2.295557 -2.217583,2.295557 v 0 c -1.224735,0 -2.217581,-1.027756 -2.217581,-2.295557 z"/><path id="path20" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="m 20.920344,32.493238 c 1.379928,0 2.069892,-0.210275 2.759856,-0.420549 0.689963,-0.210275 1.379927,-0.420548 2.759858,-0.420548"/><path id="path15" fill="#434343" fill-rule="evenodd" d="m 26.060287,21.551888 v 0 c 0,-1.267803 0.992843,-2.295558 2.217583,-2.295558 v 0 c 0.588137,0 1.152191,0.241852 1.568069,0.672353 0.415875,0.430502 0.649513,1.014385 0.649513,1.623205 v 0 c 0,1.267801 -0.992847,2.295557 -2.217582,2.295557 v 0 c -1.22474,0 -2.217583,-1.027756 -2.217583,-2.295557 z" style="stroke-width:.104845"/><path id="path16" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="m 26.060287,21.551888 v 0 c 0,-1.267803 0.992843,-2.295558 2.217583,-2.295558 v 0 c 0.588137,0 1.152191,0.241852 1.568069,0.672353 0.415875,0.430502 0.649513,1.014385 0.649513,1.623205 v 0 c 0,1.267801 -0.992847,2.295557 -2.217582,2.295557 v 0 c -1.22474,0 -2.217583,-1.027756 -2.217583,-2.295557 z"/><path id="path17" fill="#000" fill-opacity="0" fill-rule="evenodd" d="m 16.124261,30.350767 c 1.176125,0 1.764191,0.516197 2.352254,1.032392 0.588061,0.5162 1.176126,1.032396 2.352253,1.032396" style="stroke-width:.104845"/><path id="path18" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="m 16.124261,30.350767 c 1.176125,0 1.764191,0.516197 2.352254,1.032392 0.588061,0.5162 1.176126,1.032396 2.352253,1.032396"/><path id="path19" fill="#000" fill-opacity="0" fill-rule="evenodd" d="m 20.920344,32.493238 c 1.379928,0 2.069892,-0.210275 2.759856,-0.420549 0.689963,-0.210275 1.379927,-0.420548 2.759858,-0.420548" style="stroke-width:.104845"/><path style="fill:#9704ff;fill-opacity:.34854802;stroke-width:.108022" id="path21" d="M 3.5645052,62.796866 C 3.6113167,62.628582 12.372818,38.099617 12.938598,36.543302 l 0.276237,-0.759135 0.848439,0.750487 c 1.871046,1.654958 3.780284,2.690927 6.22895,3.37969 1.610931,0.452965 2.540168,0.572693 4.478365,0.574688 1.354292,0.0016 1.848306,-0.03327 2.692999,-0.172274 3.687715,-0.621914 7.078461,-2.478145 9.331288,-5.108942 l 0.593271,-0.692819 0.105003,0.225485 c 0.554477,1.190483 11.566724,27.158366 11.529936,27.189496 -0.08494,0.07117 -21.916135,0.603955 -36.430422,0.88877 -9.9347255,0.19489 -9.0927079,0.196886 -9.0278297,-0.01995 z"/></svg>
+6
backend-rs/frontend_assets/svg/avatar4.svg
··· 1 + <!-- 2 + ~ Copyright (c) 2024, MLC 'Strawmelonjuice' Bloeiman 3 + ~ 4 + ~ Licensed under the BSD 3-Clause License. See the LICENSE file for more info. 5 + --> 6 + <svg xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" id="svg20" width="50" height="50" fill="none" stroke="none" stroke-linecap="square" stroke-miterlimit="10" version="1.1" viewBox="0 0 50 50"><defs id="defs20"><clipPath id="p.0-1"><path id="path1-2" d="M 0,0 H 960 V 720 H 0 Z" clip-rule="nonzero"/></clipPath><clipPath id="p.0-1-4"><path id="path1-2-0" d="M 0,0 H 960 V 720 H 0 Z" clip-rule="nonzero"/></clipPath></defs><clipPath id="p.0"><path id="path1" d="M 0,0 H 960 V 720 H 0 Z" clip-rule="nonzero"/></clipPath><ellipse style="display:block;fill:#c4c4c4;fill-opacity:.348548;stroke:none;stroke-width:6.38572;stroke-dasharray:none;shape-rendering:auto" id="path2" cx="25.036" cy="25.14" rx="24.894" ry="24.993"/><path id="path3" fill="#000" fill-opacity="0" fill-rule="evenodd" d="M 13.675942,34.154429 3.3403878,63.076639" style="stroke-width:.104845"/><path id="path4" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="M 13.675942,34.154429 3.3403878,63.076639"/><path id="path5" fill="#000" fill-opacity="0" fill-rule="evenodd" d="M 3.3394916,63.076641 26.463401,62.627244" style="stroke-width:.104845"/><path id="path6" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="M 3.3394916,63.076641 26.463401,62.627244"/><path id="path7" fill="#000" fill-opacity="0" fill-rule="evenodd" d="M 37.137291,33.627544 49.257679,62.051776" style="stroke-width:.104845"/><path id="path8" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="M 37.137291,33.627544 49.257679,62.051776"/><path id="path9" fill="#000" fill-opacity="0" fill-rule="evenodd" d="M 49.257334,62.050424 26.137015,62.62735" style="stroke-width:.104845"/><path id="path10" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="M 49.257334,62.050424 26.137015,62.62735"/><path id="path11" fill="#cfe2f3" fill-rule="evenodd" d="m 9.6085775,26.677672 v 0 c 0,-7.605971 6.7859175,-13.771827 15.1567725,-13.771827 q 7.578387,0 15.156777,0 0,6.885913 0,13.771827 v 0 c 0,7.605972 -6.785918,13.771829 -15.156777,13.771829 v 0 c -8.370854,0 -15.1567725,-6.165857 -15.1567725,-13.771829 z" style="fill:#e0eae0;fill-opacity:1;stroke-width:.104845"/><path id="path12" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="m 9.6085775,26.677672 v 0 c 0,-7.605971 6.7859175,-13.771827 15.1567725,-13.771827 q 7.578387,0 15.156777,0 0,6.885913 0,13.771827 v 0 c 0,7.605972 -6.785918,13.771829 -15.156777,13.771829 v 0 c -8.370854,0 -15.1567725,-6.165857 -15.1567725,-13.771829 z"/><path id="path13" fill="#434343" fill-rule="evenodd" d="m 14.49534,21.551888 v 0 c 0,-1.267803 0.992846,-2.295558 2.217581,-2.295558 v 0 c 0.588139,0 1.152192,0.241852 1.568067,0.672353 0.415878,0.430502 0.649516,1.014385 0.649516,1.623205 v 0 c 0,1.267801 -0.992845,2.295557 -2.217583,2.295557 v 0 c -1.224735,0 -2.217581,-1.027756 -2.217581,-2.295557 z" style="stroke-width:.104845"/><path id="path14" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="m 14.49534,21.551888 v 0 c 0,-1.267803 0.992846,-2.295558 2.217581,-2.295558 v 0 c 0.588139,0 1.152192,0.241852 1.568067,0.672353 0.415878,0.430502 0.649516,1.014385 0.649516,1.623205 v 0 c 0,1.267801 -0.992845,2.295557 -2.217583,2.295557 v 0 c -1.224735,0 -2.217581,-1.027756 -2.217581,-2.295557 z"/><path id="path20" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="m 20.920344,32.493238 c 1.379928,0 2.069892,-0.210275 2.759856,-0.420549 0.689963,-0.210275 1.379927,-0.420548 2.759858,-0.420548"/><path id="path15" fill="#434343" fill-rule="evenodd" d="m 26.060287,21.551888 v 0 c 0,-1.267803 0.992843,-2.295558 2.217583,-2.295558 v 0 c 0.588137,0 1.152191,0.241852 1.568069,0.672353 0.415875,0.430502 0.649513,1.014385 0.649513,1.623205 v 0 c 0,1.267801 -0.992847,2.295557 -2.217582,2.295557 v 0 c -1.22474,0 -2.217583,-1.027756 -2.217583,-2.295557 z" style="stroke-width:.104845"/><path id="path16" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="m 26.060287,21.551888 v 0 c 0,-1.267803 0.992843,-2.295558 2.217583,-2.295558 v 0 c 0.588137,0 1.152191,0.241852 1.568069,0.672353 0.415875,0.430502 0.649513,1.014385 0.649513,1.623205 v 0 c 0,1.267801 -0.992847,2.295557 -2.217582,2.295557 v 0 c -1.22474,0 -2.217583,-1.027756 -2.217583,-2.295557 z"/><path id="path17" fill="#000" fill-opacity="0" fill-rule="evenodd" d="m 16.124261,30.350767 c 1.176125,0 1.764191,0.516197 2.352254,1.032392 0.588061,0.5162 1.176126,1.032396 2.352253,1.032396" style="stroke-width:.104845"/><path id="path18" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="m 16.124261,30.350767 c 1.176125,0 1.764191,0.516197 2.352254,1.032392 0.588061,0.5162 1.176126,1.032396 2.352253,1.032396"/><path id="path19" fill="#000" fill-opacity="0" fill-rule="evenodd" d="m 20.920344,32.493238 c 1.379928,0 2.069892,-0.210275 2.759856,-0.420549 0.689963,-0.210275 1.379927,-0.420548 2.759858,-0.420548" style="stroke-width:.104845"/><path style="fill:#36b2ac;fill-opacity:.34854802;stroke-width:.108022" id="path21" d="M 3.5645052,62.796866 C 3.6113167,62.628582 12.372818,38.099617 12.938598,36.543302 l 0.276237,-0.759135 0.848439,0.750487 c 1.871046,1.654958 3.780284,2.690927 6.22895,3.37969 1.610931,0.452965 2.540168,0.572693 4.478365,0.574688 1.354292,0.0016 1.848306,-0.03327 2.692999,-0.172274 3.687715,-0.621914 7.078461,-2.478145 9.331288,-5.108942 l 0.593271,-0.692819 0.105003,0.225485 c 0.554477,1.190483 11.566724,27.158366 11.529936,27.189496 -0.08494,0.07117 -21.916135,0.603955 -36.430422,0.88877 -9.9347255,0.19489 -9.0927079,0.196886 -9.0278297,-0.01995 z"/></svg>
+6
backend-rs/frontend_assets/svg/avatar5.svg
··· 1 + <!-- 2 + ~ Copyright (c) 2024, MLC 'Strawmelonjuice' Bloeiman 3 + ~ 4 + ~ Licensed under the BSD 3-Clause License. See the LICENSE file for more info. 5 + --> 6 + <svg xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" id="svg20" width="50" height="50" fill="none" stroke="none" stroke-linecap="square" stroke-miterlimit="10" version="1.1" viewBox="0 0 50 50"><defs id="defs20"><clipPath id="p.0-1"><path id="path1-2" d="M 0,0 H 960 V 720 H 0 Z" clip-rule="nonzero"/></clipPath><clipPath id="p.0-1-4"><path id="path1-2-0" d="M 0,0 H 960 V 720 H 0 Z" clip-rule="nonzero"/></clipPath></defs><clipPath id="p.0"><path id="path1" d="M 0,0 H 960 V 720 H 0 Z" clip-rule="nonzero"/></clipPath><ellipse style="display:block;fill:#c4c4c4;fill-opacity:.348548;stroke:none;stroke-width:6.38572;stroke-dasharray:none;shape-rendering:auto" id="path2" cx="25.036" cy="25.14" rx="24.894" ry="24.993"/><path id="path3" fill="#000" fill-opacity="0" fill-rule="evenodd" d="M 13.675942,34.154429 3.3403878,63.076639" style="stroke-width:.104845"/><path id="path4" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="M 13.675942,34.154429 3.3403878,63.076639"/><path id="path5" fill="#000" fill-opacity="0" fill-rule="evenodd" d="M 3.3394916,63.076641 26.463401,62.627244" style="stroke-width:.104845"/><path id="path6" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="M 3.3394916,63.076641 26.463401,62.627244"/><path id="path7" fill="#000" fill-opacity="0" fill-rule="evenodd" d="M 37.137291,33.627544 49.257679,62.051776" style="stroke-width:.104845"/><path id="path8" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="M 37.137291,33.627544 49.257679,62.051776"/><path id="path9" fill="#000" fill-opacity="0" fill-rule="evenodd" d="M 49.257334,62.050424 26.137015,62.62735" style="stroke-width:.104845"/><path id="path10" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="M 49.257334,62.050424 26.137015,62.62735"/><path id="path11" fill="#cfe2f3" fill-rule="evenodd" d="m 9.6085775,26.677672 v 0 c 0,-7.605971 6.7859175,-13.771827 15.1567725,-13.771827 q 7.578387,0 15.156777,0 0,6.885913 0,13.771827 v 0 c 0,7.605972 -6.785918,13.771829 -15.156777,13.771829 v 0 c -8.370854,0 -15.1567725,-6.165857 -15.1567725,-13.771829 z" style="fill:#643267;fill-opacity:1;stroke-width:.104845"/><path id="path12" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="m 9.6085775,26.677672 v 0 c 0,-7.605971 6.7859175,-13.771827 15.1567725,-13.771827 q 7.578387,0 15.156777,0 0,6.885913 0,13.771827 v 0 c 0,7.605972 -6.785918,13.771829 -15.156777,13.771829 v 0 c -8.370854,0 -15.1567725,-6.165857 -15.1567725,-13.771829 z"/><path id="path13" fill="#434343" fill-rule="evenodd" d="m 14.49534,21.551888 v 0 c 0,-1.267803 0.992846,-2.295558 2.217581,-2.295558 v 0 c 0.588139,0 1.152192,0.241852 1.568067,0.672353 0.415878,0.430502 0.649516,1.014385 0.649516,1.623205 v 0 c 0,1.267801 -0.992845,2.295557 -2.217583,2.295557 v 0 c -1.224735,0 -2.217581,-1.027756 -2.217581,-2.295557 z" style="stroke-width:.104845"/><path id="path14" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="m 14.49534,21.551888 v 0 c 0,-1.267803 0.992846,-2.295558 2.217581,-2.295558 v 0 c 0.588139,0 1.152192,0.241852 1.568067,0.672353 0.415878,0.430502 0.649516,1.014385 0.649516,1.623205 v 0 c 0,1.267801 -0.992845,2.295557 -2.217583,2.295557 v 0 c -1.224735,0 -2.217581,-1.027756 -2.217581,-2.295557 z"/><path id="path20" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="m 20.920344,32.493238 c 1.379928,0 2.069892,-0.210275 2.759856,-0.420549 0.689963,-0.210275 1.379927,-0.420548 2.759858,-0.420548"/><path id="path15" fill="#434343" fill-rule="evenodd" d="m 26.060287,21.551888 v 0 c 0,-1.267803 0.992843,-2.295558 2.217583,-2.295558 v 0 c 0.588137,0 1.152191,0.241852 1.568069,0.672353 0.415875,0.430502 0.649513,1.014385 0.649513,1.623205 v 0 c 0,1.267801 -0.992847,2.295557 -2.217582,2.295557 v 0 c -1.22474,0 -2.217583,-1.027756 -2.217583,-2.295557 z" style="stroke-width:.104845"/><path id="path16" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="m 26.060287,21.551888 v 0 c 0,-1.267803 0.992843,-2.295558 2.217583,-2.295558 v 0 c 0.588137,0 1.152191,0.241852 1.568069,0.672353 0.415875,0.430502 0.649513,1.014385 0.649513,1.623205 v 0 c 0,1.267801 -0.992847,2.295557 -2.217582,2.295557 v 0 c -1.22474,0 -2.217583,-1.027756 -2.217583,-2.295557 z"/><path id="path17" fill="#000" fill-opacity="0" fill-rule="evenodd" d="m 16.124261,30.350767 c 1.176125,0 1.764191,0.516197 2.352254,1.032392 0.588061,0.5162 1.176126,1.032396 2.352253,1.032396" style="stroke-width:.104845"/><path id="path18" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="m 16.124261,30.350767 c 1.176125,0 1.764191,0.516197 2.352254,1.032392 0.588061,0.5162 1.176126,1.032396 2.352253,1.032396"/><path id="path19" fill="#000" fill-opacity="0" fill-rule="evenodd" d="m 20.920344,32.493238 c 1.379928,0 2.069892,-0.210275 2.759856,-0.420549 0.689963,-0.210275 1.379927,-0.420548 2.759858,-0.420548" style="stroke-width:.104845"/><path style="fill:#6bd800;fill-opacity:.51393187;stroke-width:.108022" id="path21" d="M 3.5645052,62.796866 C 3.6113167,62.628582 12.372818,38.099617 12.938598,36.543302 l 0.276237,-0.759135 0.848439,0.750487 c 1.871046,1.654958 3.780284,2.690927 6.22895,3.37969 1.610931,0.452965 2.540168,0.572693 4.478365,0.574688 1.354292,0.0016 1.848306,-0.03327 2.692999,-0.172274 3.687715,-0.621914 7.078461,-2.478145 9.331288,-5.108942 l 0.593271,-0.692819 0.105003,0.225485 c 0.554477,1.190483 11.566724,27.158366 11.529936,27.189496 -0.08494,0.07117 -21.916135,0.603955 -36.430422,0.88877 -9.9347255,0.19489 -9.0927079,0.196886 -9.0278297,-0.01995 z"/></svg>
+6
backend-rs/frontend_assets/svg/avatar6.svg
··· 1 + <!-- 2 + ~ Copyright (c) 2024, MLC 'Strawmelonjuice' Bloeiman 3 + ~ 4 + ~ Licensed under the BSD 3-Clause License. See the LICENSE file for more info. 5 + --> 6 + <svg xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" id="svg20" width="50" height="50" fill="none" stroke="none" stroke-linecap="square" stroke-miterlimit="10" version="1.1" viewBox="0 0 50 50"><defs id="defs20"><clipPath id="p.0-1"><path id="path1-2" d="M 0,0 H 960 V 720 H 0 Z" clip-rule="nonzero"/></clipPath><clipPath id="p.0-1-4"><path id="path1-2-0" d="M 0,0 H 960 V 720 H 0 Z" clip-rule="nonzero"/></clipPath></defs><clipPath id="p.0"><path id="path1" d="M 0,0 H 960 V 720 H 0 Z" clip-rule="nonzero"/></clipPath><ellipse style="display:block;fill:#c4c4c4;fill-opacity:.348548;stroke:none;stroke-width:6.38572;stroke-dasharray:none;shape-rendering:auto" id="path2" cx="25.036" cy="25.14" rx="24.894" ry="24.993"/><path id="path3" fill="#000" fill-opacity="0" fill-rule="evenodd" d="M 13.675942,34.154429 3.3403878,63.076639" style="stroke-width:.104845"/><path id="path4" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="M 13.675942,34.154429 3.3403878,63.076639"/><path id="path5" fill="#000" fill-opacity="0" fill-rule="evenodd" d="M 3.3394916,63.076641 26.463401,62.627244" style="stroke-width:.104845"/><path id="path6" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="M 3.3394916,63.076641 26.463401,62.627244"/><path id="path7" fill="#000" fill-opacity="0" fill-rule="evenodd" d="M 37.137291,33.627544 49.257679,62.051776" style="stroke-width:.104845"/><path id="path8" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="M 37.137291,33.627544 49.257679,62.051776"/><path id="path9" fill="#000" fill-opacity="0" fill-rule="evenodd" d="M 49.257334,62.050424 26.137015,62.62735" style="stroke-width:.104845"/><path id="path10" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="M 49.257334,62.050424 26.137015,62.62735"/><path id="path11" fill="#cfe2f3" fill-rule="evenodd" d="m 9.6085775,26.677672 v 0 c 0,-7.605971 6.7859175,-13.771827 15.1567725,-13.771827 q 7.578387,0 15.156777,0 0,6.885913 0,13.771827 v 0 c 0,7.605972 -6.785918,13.771829 -15.156777,13.771829 v 0 c -8.370854,0 -15.1567725,-6.165857 -15.1567725,-13.771829 z" style="fill:#454;fill-opacity:1;stroke-width:.104845"/><path id="path12" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="m 9.6085775,26.677672 v 0 c 0,-7.605971 6.7859175,-13.771827 15.1567725,-13.771827 q 7.578387,0 15.156777,0 0,6.885913 0,13.771827 v 0 c 0,7.605972 -6.785918,13.771829 -15.156777,13.771829 v 0 c -8.370854,0 -15.1567725,-6.165857 -15.1567725,-13.771829 z"/><path id="path13" fill="#434343" fill-rule="evenodd" d="m 14.49534,21.551888 v 0 c 0,-1.267803 0.992846,-2.295558 2.217581,-2.295558 v 0 c 0.588139,0 1.152192,0.241852 1.568067,0.672353 0.415878,0.430502 0.649516,1.014385 0.649516,1.623205 v 0 c 0,1.267801 -0.992845,2.295557 -2.217583,2.295557 v 0 c -1.224735,0 -2.217581,-1.027756 -2.217581,-2.295557 z" style="stroke-width:.104845"/><path id="path14" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="m 14.49534,21.551888 v 0 c 0,-1.267803 0.992846,-2.295558 2.217581,-2.295558 v 0 c 0.588139,0 1.152192,0.241852 1.568067,0.672353 0.415878,0.430502 0.649516,1.014385 0.649516,1.623205 v 0 c 0,1.267801 -0.992845,2.295557 -2.217583,2.295557 v 0 c -1.224735,0 -2.217581,-1.027756 -2.217581,-2.295557 z"/><path id="path20" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="m 20.920344,32.493238 c 1.379928,0 2.069892,-0.210275 2.759856,-0.420549 0.689963,-0.210275 1.379927,-0.420548 2.759858,-0.420548"/><path id="path15" fill="#434343" fill-rule="evenodd" d="m 26.060287,21.551888 v 0 c 0,-1.267803 0.992843,-2.295558 2.217583,-2.295558 v 0 c 0.588137,0 1.152191,0.241852 1.568069,0.672353 0.415875,0.430502 0.649513,1.014385 0.649513,1.623205 v 0 c 0,1.267801 -0.992847,2.295557 -2.217582,2.295557 v 0 c -1.22474,0 -2.217583,-1.027756 -2.217583,-2.295557 z" style="stroke-width:.104845"/><path id="path16" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="m 26.060287,21.551888 v 0 c 0,-1.267803 0.992843,-2.295558 2.217583,-2.295558 v 0 c 0.588137,0 1.152191,0.241852 1.568069,0.672353 0.415875,0.430502 0.649513,1.014385 0.649513,1.623205 v 0 c 0,1.267801 -0.992847,2.295557 -2.217582,2.295557 v 0 c -1.22474,0 -2.217583,-1.027756 -2.217583,-2.295557 z"/><path id="path17" fill="#000" fill-opacity="0" fill-rule="evenodd" d="m 16.124261,30.350767 c 1.176125,0 1.764191,0.516197 2.352254,1.032392 0.588061,0.5162 1.176126,1.032396 2.352253,1.032396" style="stroke-width:.104845"/><path id="path18" fill-rule="evenodd" stroke="#000" stroke-linecap="butt" stroke-linejoin="round" stroke-width=".105" d="m 16.124261,30.350767 c 1.176125,0 1.764191,0.516197 2.352254,1.032392 0.588061,0.5162 1.176126,1.032396 2.352253,1.032396"/><path id="path19" fill="#000" fill-opacity="0" fill-rule="evenodd" d="m 20.920344,32.493238 c 1.379928,0 2.069892,-0.210275 2.759856,-0.420549 0.689963,-0.210275 1.379927,-0.420548 2.759858,-0.420548" style="stroke-width:.104845"/><path style="fill:#d89800;fill-opacity:.51393187;stroke-width:.108022" id="path21" d="M 3.5645052,62.796866 C 3.6113167,62.628582 12.372818,38.099617 12.938598,36.543302 l 0.276237,-0.759135 0.848439,0.750487 c 1.871046,1.654958 3.780284,2.690927 6.22895,3.37969 1.610931,0.452965 2.540168,0.572693 4.478365,0.574688 1.354292,0.0016 1.848306,-0.03327 2.692999,-0.172274 3.687715,-0.621914 7.078461,-2.478145 9.331288,-5.108942 l 0.593271,-0.692819 0.105003,0.225485 c 0.554477,1.190483 11.566724,27.158366 11.529936,27.189496 -0.08494,0.07117 -21.916135,0.603955 -36.430422,0.88877 -9.9347255,0.19489 -9.0927079,0.196886 -9.0278297,-0.01995 z"/></svg>
+6
backend-rs/frontend_assets/svg/bell.svg
··· 1 + <!-- 2 + ~ Copyright (c) 2024, MLC 'Strawmelonjuice' Bloeiman 3 + ~ 4 + ~ Licensed under the BSD 3-Clause License. See the LICENSE file for more info. 5 + --> 6 + <svg xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" id="svg1" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true" class="inline w-6 h-6" version="1.1" viewBox="0 0 24 24"><path id="path1" stroke-linecap="round" stroke-linejoin="round" d="M14.857 17.082a23.848 23.848 0 005.454-1.31A8.967 8.967 0 0118 9.75v-.7V9A6 6 0 006 9v.75a8.967 8.967 0 01-2.312 6.022c1.733.64 3.56 1.085 5.455 1.31m5.714 0a24.255 24.255 0 01-5.714 0m5.714 0a3 3 0 11-5.714 0"/><ellipse style="stroke-opacity:1;stroke-dasharray:none;stroke-width:.9;stroke:#000;fill-rule:evenodd;fill:red" id="path2" cx="16.526" cy="19.078" class="svg_activenotification" rx="1.919" ry="1.817"/></svg>
+7
backend-rs/frontend_assets/svg/boost.svg
··· 1 + <?xml version="1.0" encoding="UTF-8"?> 2 + <!-- 3 + ~ Copyright (c) 2024, MLC 'Strawmelonjuice' Bloeiman 4 + ~ 5 + ~ Licensed under the BSD 3-Clause License. See the LICENSE file for more info. 6 + --> 7 + <svg width="120" height="120" version="1.1" viewBox="0 0 120 120" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><ellipse cx="58.299" cy="60.059" rx="42.156" ry="40.068" fill-opacity=".098814" opacity=".85" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.1034"/><g transform="matrix(-.46196 -.12948 -.14495 .51715 104.99 42.454)" fill-opacity=".9885" opacity=".85" stroke="currentColor" stroke-linecap="square"><rect transform="matrix(-.61519 -.78838 .72963 -.68384 0 0)" x="-140.46" y="5.9193" width="2.0121" height="14.936" ry=".059533" stroke-width="4.5232" style="paint-order:fill markers stroke"/><rect x="21.656" y="25.317" width="3.2053" height="67.868" ry=".27051" stroke-width="4.514" style="paint-order:fill markers stroke"/><rect transform="matrix(-.61519 .78838 .72963 .68384 0 0)" x="32.372" y="69.376" width="2.0121" height="14.936" ry=".059533" stroke-width="4.5232" style="paint-order:fill markers stroke"/><rect transform="rotate(90)" x="89.925" y="-88.832" width="3.2437" height="67.064" ry=".2673" stroke-width="4.514" style="paint-order:fill markers stroke"/><rect transform="matrix(-.64475 -.76439 .75555 -.6551 0 0)" x="-150.67" y="-2.4582" width="3.5364" height="26.57" ry=".1059" stroke-width="4.5142" style="paint-order:fill markers stroke"/><rect transform="matrix(-.61519 .78838 .72963 .68384 0 0)" x="-6.9496" y="119.46" width="2.0121" height="14.936" ry=".059533" stroke-width="4.5227" style="paint-order:fill markers stroke"/><rect transform="matrix(-.64475 .76439 .75555 .6551 0 0)" x="-12.385" y="115.05" width="3.5364" height="26.57" ry=".1059" stroke-width="4.5142" style="paint-order:fill markers stroke"/></g><g transform="matrix(.46196 .12948 .14495 -.51715 13.238 80.586)" fill-opacity=".9885" opacity=".85" stroke="currentColor" stroke-linecap="square"><rect transform="matrix(-.61519 -.78838 .72963 -.68384 0 0)" x="-140.46" y="5.9193" width="2.0121" height="14.936" ry=".059533" stroke-width="4.5232" style="paint-order:fill markers stroke"/><rect x="21.656" y="25.317" width="3.2053" height="67.868" ry=".27051" stroke-width="4.514" style="paint-order:fill markers stroke"/><rect transform="matrix(-.61519 .78838 .72963 .68384 0 0)" x="32.372" y="69.376" width="2.0121" height="14.936" ry=".059533" stroke-width="4.5232" style="paint-order:fill markers stroke"/><rect transform="rotate(90)" x="89.925" y="-88.832" width="3.2437" height="67.064" ry=".2673" stroke-width="4.514" style="paint-order:fill markers stroke"/><rect transform="matrix(-.64475 -.76439 .75555 -.6551 0 0)" x="-150.67" y="-2.4582" width="3.5364" height="26.57" ry=".1059" stroke-width="4.5142" style="paint-order:fill markers stroke"/><rect transform="matrix(-.61519 .78838 .72963 .68384 0 0)" x="-6.9496" y="119.46" width="2.0121" height="14.936" ry=".059533" stroke-width="4.5227" style="paint-order:fill markers stroke"/><rect transform="matrix(-.64475 .76439 .75555 .6551 0 0)" x="-12.385" y="115.05" width="3.5364" height="26.57" ry=".1059" stroke-width="4.5142" style="paint-order:fill markers stroke"/></g></svg>
+17
backend-rs/frontend_assets/svg/comment.svg
··· 1 + <?xml version="1.0" encoding="UTF-8"?> 2 + <!-- 3 + ~ Copyright (c) 2024, MLC 'Strawmelonjuice' Bloeiman 4 + ~ 5 + ~ Licensed under the BSD 3-Clause License. See the LICENSE file for more info. 6 + --> 7 + <svg width="120" height="120" version="1.1" viewBox="0 0 120 120" xmlns="http://www.w3.org/2000/svg"> 8 + <g transform="matrix(-.70451 0 0 .69982 104.54 6.742)" fill-opacity=".9885" stroke="currentColor" stroke-linecap="square"> 9 + <rect transform="matrix(-.61519 -.78838 .72963 -.68384 0 0)" x="-140.46" y="5.9193" width="2.0121" height="14.936" ry=".059533" stroke-width="4.5232" style="paint-order:fill markers stroke"/> 10 + <rect x="21.656" y="25.317" width="3.2053" height="67.868" ry=".27051" stroke-width="4.514" style="paint-order:fill markers stroke"/> 11 + <rect transform="matrix(-.61519 .78838 .72963 .68384 0 0)" x="32.372" y="69.376" width="2.0121" height="14.936" ry=".059533" stroke-width="4.5232" style="paint-order:fill markers stroke"/> 12 + <rect transform="rotate(90)" x="89.925" y="-88.832" width="3.2437" height="67.064" ry=".2673" stroke-width="4.514" style="paint-order:fill markers stroke"/> 13 + <rect transform="matrix(-.64475 -.76439 .75555 -.6551 0 0)" x="-150.67" y="-2.4582" width="3.5364" height="26.57" ry=".1059" stroke-width="4.5142" style="paint-order:fill markers stroke"/> 14 + <rect transform="matrix(-.61519 .78838 .72963 .68384 0 0)" x="-6.9496" y="119.46" width="2.0121" height="14.936" ry=".059533" stroke-width="4.5227" style="paint-order:fill markers stroke"/> 15 + <rect transform="matrix(-.64475 .76439 .75555 .6551 0 0)" x="-12.385" y="115.05" width="3.5364" height="26.57" ry=".1059" stroke-width="4.5142" style="paint-order:fill markers stroke"/> 16 + </g> 17 + </svg>
+6
backend-rs/frontend_assets/svg/green_check.svg
··· 1 + <!-- 2 + ~ Copyright (c) 2024, MLC 'Strawmelonjuice' Bloeiman 3 + ~ 4 + ~ Licensed under the BSD 3-Clause License. See the LICENSE file for more info. 5 + --> 6 + <svg xmlns="http://www.w3.org/2000/svg" style="display:block;shape-rendering:auto" id="svg12" width="10" height="10" preserveAspectRatio="xMidYMid" version="1.1" viewBox="0 0 5 5"><defs id="defs12"><linearGradient id="linearGradient7"><stop style="stop-color:#d3d3d3;stop-opacity:1" id="stop8" offset="0"/><stop style="stop-color:#d3d3d3;stop-opacity:0" id="stop9" offset="1"/></linearGradient></defs><ellipse style="display:block;shape-rendering:auto;fill:#c4c4c4;stroke-width:.6375;fill-opacity:.34854773;stroke-dasharray:none;stroke:none" id="path2" cx="2.511" cy="2.511" rx="2.483" ry="2.497"/><path style="fill:#0f0;stroke-width:.323941" id="path1" d="m 1.3269245,1.5156002 c 0.1393806,-0.00489 0.2772916,-0.029078 0.4152688,-0.048014 0.025488,-0.0035 0.081815,-0.011673 0.1025075,-0.00509 0.00293,9.347e-4 -0.00115,0.00702 0.00132,0.0088 0.00526,0.00377 0.022336,-0.00756 0.027597,-0.00379 0.00495,0.00355 0.00176,0.011729 0.00264,0.0176 0.00397,0.026466 3.55e-4,0.04936 -0.00784,0.072923 -0.00622,0.017874 0.011863,0.062224 0.011876,0.079199 1.91e-5,0.03279 -0.00538,0.065736 -0.00389,0.099323 0.003,0.066958 0.020415,0.1361167 0.030355,0.2023963 0.021997,0.1466642 0.043993,0.2933281 0.065989,0.4399921 0.015182,0.1012244 0.022315,0.208489 0.046192,0.3079945 0.010398,0.043339 0.02408,0.086978 0.038194,0.1294718 0.0047,0.01412 0.00252,0.029707 0.0066,0.044 0.00813,0.028486 0.02459,0.057255 0.031595,0.085473 0.00815,0.032823 0.00509,0.097506 0.019796,0.1319979 0.00422,0.0099 0.012928,-0.018339 0.014437,-0.028925 0.00763,-0.053416 0.035616,-0.1040133 0.061673,-0.1521602 0.00949,-0.017528 0.038442,-0.060164 0.044635,-0.077974 0.00196,-0.00563 -0.0041,-0.011839 -0.00264,-0.017601 0.0098,-0.038652 0.014972,-0.063276 0.022278,-0.101848 0.00567,-0.029961 0.034861,-0.049865 0.044636,-0.077974 0.00262,-0.00754 -0.00659,-0.018855 -0.00397,-0.0264 0.00553,-0.015906 0.023101,-0.025547 0.031516,-0.04025 0.00666,-0.011618 0.00715,-0.025766 0.013114,-0.037725 0.02929,-0.058677 0.1052461,-0.104809 0.1365842,-0.1534607 0.00514,-0.00798 0.00144,-0.019023 0.00525,-0.027662 0.013087,-0.029798 0.039744,-0.057152 0.052515,-0.088035 0.00361,-0.00871 8.213e-4,-0.0193 0.00525,-0.027662 0.025269,-0.047798 0.066769,-0.090794 0.09191,-0.1383482 0.024264,-0.045897 0.041335,-0.096475 0.062994,-0.1433602 0.017293,-0.037435 0.051758,-0.063635 0.069592,-0.099362 0.00133,-0.00267 -0.00318,-0.00643 -0.00132,-0.0088 0.00186,-0.00237 0.00734,0.00111 0.0092,-0.00126 0.00371,-0.00474 -0.00635,-0.012857 -0.00264,-0.0176 0.00185,-0.00237 0.00734,0.00111 0.0092,-0.00126 0.00186,-0.00237 -0.0023,-0.00599 -0.00132,-0.0088 0.00138,-0.00398 0.00526,-0.0067 0.00788,-0.010063 0.00349,-0.018441 0.00515,-0.03729 0.010478,-0.055325 0.00608,-0.020562 0.017806,-0.03929 0.028877,-0.057848 0.014154,-0.023728 0.033648,-0.04452 0.045955,-0.069173 0.00298,-0.00599 0.00241,-0.013559 0.00656,-0.018862 0.00415,-0.0053 0.012928,-0.00602 0.017079,-0.011329 0.0067,-0.00855 0.00775,-0.020373 0.014437,-0.028925 0.012408,-0.015849 0.053261,-0.04705 0.065672,-0.062899 0.011543,-0.014747 0.010385,-0.040356 0.019678,-0.056586 0.024247,-0.042359 0.050559,-0.084621 0.074831,-0.127023 0.00276,-0.00482 0.00168,-0.044078 0.0026,-0.045262 0.00813,-0.010393 0.070227,-0.00964 0.08279,-0.011357 0.050123,-0.00688 0.2347721,-0.035977 0.2391704,-0.032826 0.00249,0.00178 -0.00116,0.00702 0.00132,0.0088 0.00318,0.00227 0.054872,-0.00753 0.055193,-0.00757 0.042975,-0.00589 0.2015482,-0.044077 0.2312916,-0.022763 0.00965,0.00692 0.00877,0.058479 0.010558,0.070399 0.00847,0.05647 -0.011556,0.1619356 -0.053754,0.2049592 -0.029007,0.029577 -0.061027,0.054924 -0.090625,0.084284 -0.021287,0.021117 -0.03536,0.04803 -0.055153,0.070437 -0.017328,0.019613 -0.090118,0.081583 -0.095869,0.1119479 -0.00305,0.016112 4.505e-4,0.029151 -0.0026,0.045262 -0.00278,0.014692 -0.01917,0.030801 -0.020997,0.047787 -0.00262,0.024357 -0.00726,0.048285 -0.00784,0.072923 0.035952,0.029065 -0.010092,0.03065 -0.011796,0.046524 -0.00127,0.011784 0.0075,0.023535 0.00529,0.035199 -0.0075,0.039597 -0.00397,0.1121668 -0.01568,0.1458477 -0.00414,0.011935 -0.017595,0.019023 -0.023637,0.030187 -0.017663,0.032636 -0.030201,0.096274 -0.045915,0.1320351 -0.01133,0.025771 -0.022,0.051886 -0.035436,0.076712 -0.010065,0.018607 -0.030908,0.030995 -0.039395,0.050311 -0.00379,0.00865 -0.00182,0.018881 -0.00525,0.027662 -0.1090605,0.2798502 0.014303,-0.048289 -0.057754,0.1156978 -0.01826,0.041556 -0.031432,0.090389 -0.045915,0.132035 -0.00887,0.025486 0.00272,0.055171 -0.00652,0.081724 -0.00489,0.014059 -0.016795,0.025145 -0.022318,0.038986 -0.00595,0.014908 -0.00534,0.031813 -0.011796,0.046524 -0.031416,0.071497 -0.091898,0.1278488 -0.1405037,0.1899225 -0.045895,0.058612 -0.086435,0.1208723 -0.1326244,0.1798601 -0.027898,0.035627 -0.055299,0.07739 -0.08667,0.1106856 -0.00748,0.00794 -0.018261,0.01283 -0.024958,0.021387 -0.051467,0.065728 0.050898,-0.035662 -0.022318,0.038986 -0.038362,0.039113 -0.067069,0.080493 -0.1037483,0.1220108 -0.0072,0.00816 -0.018967,0.012375 -0.024958,0.021387 -0.00969,0.014584 -0.012129,0.032734 -0.020998,0.047787 -0.048236,0.081878 -0.054868,0.074472 -0.1089873,0.1496729 -0.1177969,0.1636876 0.035137,-0.04153 -0.066952,0.1169615 -0.00493,0.00765 -0.020753,0.00553 -0.026277,0.012582 -0.00262,0.00336 -0.00261,0.024308 -0.00525,0.027662 -0.00123,0.00157 -0.035157,0.00622 -0.036795,0.00505 -0.00248,-0.00178 0.00116,-0.00702 -0.00132,-0.0088 -0.00389,-0.00278 -0.064894,-0.024554 -0.069672,-0.026362 -0.045239,-0.017118 -0.041467,-0.010049 -0.077551,-0.016299 -0.00863,-0.0015 -0.012728,-0.01243 -0.021037,-0.015075 -0.00929,-0.00296 -0.019887,-0.00138 -0.028917,-0.00501 -0.027662,-0.011119 -0.05166,-0.033196 -0.081506,-0.042695 -0.035476,-0.011288 -0.080173,-0.015408 -0.116985,-0.028849 -0.054014,-0.019721 -0.1042196,-0.053369 -0.1603798,-0.067798 -0.00598,-0.00153 -0.012515,0.00439 -0.018398,0.00252 -0.00421,-0.00134 -0.020109,-0.054248 -0.019757,-0.069136 0.00127,-0.053764 0.021508,-0.1135489 0.031438,-0.1659723 0.00815,-0.042999 -5.144e-4,-0.111949 -0.00404,-0.1521235 C 1.664044,3.1233123 1.6246767,2.9375429 1.5971214,2.7538145 1.5879482,2.692646 1.5666183,2.6222172 1.5680868,2.5602179 1.5697777,2.4886934 1.5756091,2.4160872 1.5732126,2.3439717 1.570723,2.269314 1.5499403,2.1888002 1.5388979,2.1151758 1.5311626,2.0636007 1.5306434,1.998637 1.5138215,1.9479789 1.4964698,1.8957362 1.4757364,1.8468165 1.4624693,1.7933694 1.4517651,1.750263 1.4353479,1.687349 1.440033,1.6437721 c 4.488e-4,-0.00417 0.0071,-0.00593 0.00788,-0.010063 0.00429,-0.022602 -0.010078,-0.062767 -0.021077,-0.077937 -0.00353,-0.00487 0.00232,-0.014047 -0.00264,-0.0176 -0.00526,-0.00377 -0.014459,-0.00251 -0.019718,-0.00628 -0.00495,-0.00355 0.00232,-0.014047 -0.00264,-0.0176 -0.00526,-0.00377 -0.014459,-0.00251 -0.019718,-0.00628 -0.00248,-0.00178 0.00174,-0.00922 -0.00132,-0.0088 -0.018651,0.00256 -0.035915,0.010916 -0.053874,0.016375 z"/></svg>
+100
backend-rs/frontend_assets/svg/luminalogo-0.svg
··· 1 + <?xml version="1.0" encoding="UTF-8" standalone="no"?> 2 + <!-- 3 + ~ Copyright (c) 2024, MLC 'Strawmelonjuice' Bloeiman 4 + ~ 5 + ~ Licensed under the BSD 3-Clause License. See the LICENSE file for more info. 6 + --> 7 + 8 + <svg 9 + id="svg1" 10 + width="120" 11 + height="120" 12 + version="1.1" 13 + viewBox="0 0 120 120" 14 + sodipodi:docname="luminalogo-0.svg" 15 + inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)" 16 + inkscape:export-filename="../png/luminalogo-0.png" 17 + inkscape:export-xdpi="96" 18 + inkscape:export-ydpi="96" 19 + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" 20 + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" 21 + xmlns="http://www.w3.org/2000/svg" 22 + xmlns:svg="http://www.w3.org/2000/svg"> 23 + <defs 24 + id="defs1" /> 25 + <sodipodi:namedview 26 + id="namedview1" 27 + pagecolor="#ffffff" 28 + bordercolor="#000000" 29 + borderopacity="0.25" 30 + inkscape:showpageshadow="2" 31 + inkscape:pageopacity="0.0" 32 + inkscape:pagecheckerboard="0" 33 + inkscape:deskcolor="#d1d1d1" 34 + inkscape:zoom="3.2785512" 35 + inkscape:cx="14.3356" 36 + inkscape:cy="36.601533" 37 + inkscape:window-width="1920" 38 + inkscape:window-height="1128" 39 + inkscape:window-x="0" 40 + inkscape:window-y="0" 41 + inkscape:window-maximized="1" 42 + inkscape:current-layer="svg1" /> 43 + <g 44 + id="layer1" 45 + transform="translate(-1.1977755,1.3276101)"> 46 + <ellipse 47 + style="fill:#ffcc00" 48 + id="path1" 49 + cx="32.622002" 50 + cy="99.763" 51 + rx="14.187" 52 + ry="13.374" /> 53 + <ellipse 54 + style="fill:#000080" 55 + id="path2" 56 + cx="107.5645" 57 + cy="46.181362" 58 + rx="13.374" 59 + ry="15.091" /> 60 + <ellipse 61 + style="fill:#4d4d4d" 62 + id="path3" 63 + cx="26.115999" 64 + cy="28.646" 65 + rx="26.837999" 66 + ry="27.561001" /> 67 + <ellipse 68 + style="fill:#d38d5f" 69 + id="path4" 70 + cx="78.708" 71 + cy="30.181999" 72 + rx="6.5970001" 73 + ry="6.3260002" /> 74 + <path 75 + style="fill:#000000" 76 + id="path8" 77 + d="m 28.555366,54.21905 c 4.698985,33.615811 4.698985,33.615811 4.698985,33.615811" /> 78 + <path 79 + style="fill:#ffaeae;fill-opacity:1" 80 + id="path12" 81 + d="m 26.748065,29.459017 9.036508,67.231623 -0.72292,0.18073" /> 82 + <path 83 + style="fill:#790000;fill-opacity:1;stroke-width:2.16341" 84 + id="path14" 85 + d="m 112.07197,52.140484 -80.699252,-12.786469 2.519513,5.460883 v 0 0" 86 + sodipodi:nodetypes="ccccc" /> 87 + <path 88 + style="fill:#cd0909;fill-opacity:1" 89 + id="path13" 90 + d="m 35.423113,96.69064 42.83305,-65.424321 0.90365,0.542191 -42.833049,66.147241 h -0.18073 z" /> 91 + <path 92 + style="fill:#ff0000;stroke-width:3.57723" 93 + id="path15" 94 + d="M 82.607667,34.648745 111.7959,52.394116 v 0 L 78.614522,29.156547 Z" /> 95 + <path 96 + style="fill:#b94646;fill-opacity:1" 97 + id="path16" 98 + d="m 74.641559,29.820478 -39.760637,7.409937 v 0 l 38.676256,-9.397969 v 0 0 z" /> 99 + </g> 100 + </svg>
+109
backend-rs/frontend_assets/svg/luminalogo-1.svg
··· 1 + <?xml version="1.0" encoding="UTF-8" standalone="no"?> 2 + <!-- 3 + ~ Copyright (c) 2024, MLC 'Strawmelonjuice' Bloeiman 4 + ~ 5 + ~ Licensed under the BSD 3-Clause License. See the LICENSE file for more info. 6 + --> 7 + 8 + <svg 9 + id="svg1" 10 + width="120" 11 + height="120" 12 + version="1.1" 13 + viewBox="0 0 120 120" 14 + sodipodi:docname="luminalogo-1.svg" 15 + inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)" 16 + inkscape:export-filename="../png/luminalogo-1.png" 17 + inkscape:export-xdpi="96" 18 + inkscape:export-ydpi="96" 19 + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" 20 + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" 21 + xmlns="http://www.w3.org/2000/svg" 22 + xmlns:svg="http://www.w3.org/2000/svg"> 23 + <defs 24 + id="defs1" /> 25 + <sodipodi:namedview 26 + id="namedview1" 27 + pagecolor="#ffffff" 28 + bordercolor="#000000" 29 + borderopacity="0.25" 30 + inkscape:showpageshadow="2" 31 + inkscape:pageopacity="0.0" 32 + inkscape:pagecheckerboard="0" 33 + inkscape:deskcolor="#d1d1d1" 34 + inkscape:zoom="4.112263" 35 + inkscape:cx="57.875675" 36 + inkscape:cy="49.607721" 37 + inkscape:window-width="1920" 38 + inkscape:window-height="1128" 39 + inkscape:window-x="0" 40 + inkscape:window-y="0" 41 + inkscape:window-maximized="1" 42 + inkscape:current-layer="svg1" /> 43 + <g 44 + id="layer2" 45 + style="display:inline;fill:#ff6f08;fill-opacity:0.524951" 46 + transform="translate(10.078565,21.743467)"> 47 + <path 48 + style="fill:#ff6f08;fill-opacity:0.524951" 49 + id="path6" 50 + d="m 59.920493,35.97327 c 0.02434,0.290094 0.06487,0.579283 0.07303,0.870282 0.01131,0.403437 -0.0064,0.807188 -0.01469,1.210697 -0.05537,2.686992 -0.310969,5.349396 -0.604281,8.019455 -0.106169,2.837652 -0.638764,5.639873 -0.814527,8.470747 -0.04226,0.680659 -0.07323,2.790624 -0.08607,3.501434 0.0277,2.689403 -8.45e-4,5.379505 -0.125408,8.066236 -0.09936,1.501992 0.01475,3.011357 0.197177,4.502895 0.18554,1.065579 0.406733,2.12569 0.658557,3.177578 0.233487,0.829929 0.428099,1.672688 0.697953,2.4918 0.15861,0.674107 0.418834,1.299222 0.727503,1.915881 0.521263,0.912555 1.196993,1.709777 1.883529,2.499603 0.639982,0.672658 1.363774,1.299117 2.168539,1.769866 12.558905,7.34636 8.506542,5.38692 13.682471,7.860804 0.665028,0.338812 1.374121,0.551272 2.082153,0.77281 0.438852,0.137313 0.703704,0.256905 1.149913,0.34846 0.177844,0.03649 0.359571,0.05048 0.539356,0.07573 0.518886,0.0311 1.040244,0.104878 1.5614,0.08885 0.497768,-0.01531 0.989018,-0.11971 1.479493,-0.19632 0.659206,-0.181827 1.298249,-0.439176 1.917167,-0.729161 0.307243,-0.133551 0.607881,-0.290232 0.927638,-0.393461 0.135487,-0.04374 0.27761,-0.06488 0.412977,-0.108988 1.890769,-0.616103 -0.88541,0.226671 0.938814,-0.319168 0.377858,-0.131535 0.778978,-0.142572 1.168105,-0.209501 0.214806,-0.03695 0.425372,-0.09579 0.639657,-0.135645 0,0 -12.209607,-8.45218 -12.209607,-8.45218 v 0 c -0.581512,0.164075 -1.167576,0.309306 -1.728315,0.540912 -0.842263,0.281829 -1.708842,0.493059 -2.530454,0.836332 -0.511785,0.204425 -1.015543,0.435988 -1.576605,0.465593 -0.892182,0.09574 -1.79237,-0.03477 -2.659459,-0.252228 -1.159405,-0.36404 -2.314346,-0.742668 -3.367466,-1.365985 -4.542034,-2.579233 -0.799218,-0.456481 11.000309,6.391652 0.21726,0.126092 -0.437465,-0.24722 -0.652055,-0.377806 -0.268427,-0.163349 -0.753488,-0.482226 -1.018052,-0.678238 -0.139255,-0.103172 -0.271663,-0.21528 -0.407494,-0.322919 -0.394614,-0.35058 -0.753781,-0.719271 -1.107072,-1.111628 -0.02426,-0.02695 -0.540442,-0.611006 -0.582238,-0.67203 -0.07216,-0.105355 -0.119835,-0.225532 -0.179752,-0.338298 -0.186438,-0.243567 -0.312176,-0.486041 -0.465793,-0.748918 -0.159681,-0.273254 -0.34586,-0.526382 -0.411041,-0.84603 -0.258232,-0.838446 -0.627088,-1.640644 -0.869039,-2.486231 -0.291198,-1.025474 -0.507349,-2.072221 -0.767053,-3.106035 -0.02222,-0.09355 -0.276309,-1.16084 -0.289439,-1.22702 -0.211621,-1.06661 -0.281172,-2.157808 -0.361743,-3.239794 -0.202839,-2.776164 -0.369193,-5.556343 -0.435037,-8.33941 -0.134457,-4.010728 -0.185302,-8.049303 0.412949,-12.028841 0.394999,-2.900178 0.828375,-5.83255 1.755081,-8.621441 0.07983,-0.24024 0.185911,-0.470944 0.278866,-0.706415 z" /> 51 + </g> 52 + <g 53 + id="layer1-5" 54 + transform="translate(0.73545524,-0.09329462)"> 55 + <ellipse 56 + style="fill:#ffcc00" 57 + id="path1-3" 58 + cx="32.622002" 59 + cy="99.763" 60 + rx="14.187" 61 + ry="13.374" /> 62 + <ellipse 63 + style="fill:#000080" 64 + id="path2-5" 65 + cx="107.5645" 66 + cy="46.181362" 67 + rx="13.374" 68 + ry="15.091" /> 69 + <ellipse 70 + style="fill:#4d4d4d" 71 + id="path3-6" 72 + cx="26.115999" 73 + cy="28.646" 74 + rx="26.837999" 75 + ry="27.561001" /> 76 + <ellipse 77 + style="fill:#d38d5f" 78 + id="path4-2" 79 + cx="78.708" 80 + cy="30.181999" 81 + rx="6.5970001" 82 + ry="6.3260002" /> 83 + <path 84 + style="fill:#000000" 85 + id="path8-9" 86 + d="m 28.555366,54.21905 c 4.698985,33.615811 4.698985,33.615811 4.698985,33.615811" /> 87 + <path 88 + style="fill:#ffaeae;fill-opacity:1" 89 + id="path12-1" 90 + d="m 26.748065,29.459017 9.036508,67.231623 -0.72292,0.18073" /> 91 + <path 92 + style="fill:#790000;fill-opacity:1;stroke-width:2.16341" 93 + id="path14-2" 94 + d="m 112.07197,52.140484 -80.699252,-12.786469 2.519513,5.460883 v 0 0" 95 + sodipodi:nodetypes="ccccc" /> 96 + <path 97 + style="fill:#cd0909;fill-opacity:1" 98 + id="path13-7" 99 + d="m 35.423113,96.69064 42.83305,-65.424321 0.90365,0.542191 -42.833049,66.147241 h -0.18073 z" /> 100 + <path 101 + style="fill:#ff0000;stroke-width:3.57723" 102 + id="path15-0" 103 + d="M 82.607667,34.648745 111.7959,52.394116 v 0 L 78.614522,29.156547 Z" /> 104 + <path 105 + style="fill:#b94646;fill-opacity:1" 106 + id="path16-9" 107 + d="m 74.641559,29.820478 -39.760637,7.409937 v 0 l 38.676256,-9.397969 v 0 0 z" /> 108 + </g> 109 + </svg>
+21
backend-rs/frontend_assets/svg/push.svg
··· 1 + <?xml version="1.0" encoding="UTF-8"?> 2 + <!-- 3 + ~ Copyright (c) 2024, MLC 'Strawmelonjuice' Bloeiman 4 + ~ 5 + ~ Licensed under the BSD 3-Clause License. See the LICENSE file for more info. 6 + --> 7 + <svg width="120" height="120" version="1.1" viewBox="0 0 120 120" xmlns="http://www.w3.org/2000/svg"> 8 + <g transform="matrix(.66012 0 0 .80825 20.221 10.123)" fill="currentColor" stroke="currentColor" stroke-width="0"> 9 + <rect x="42.838" y="52.905" width="35.279" height="54.95" ry="7.0208" stroke-linecap="square" stroke-linejoin="bevel" style="paint-order:markers fill stroke"/> 10 + <g stroke-miterlimit="25.4"> 11 + <rect transform="matrix(-.79708 -.60388 -.60082 .79938 0 0)" x="-123.04" y="-24.688" width="59.5" height="6.6488" rx=".6593" ry="1.9825" style="paint-order:markers fill stroke"/> 12 + <rect transform="matrix(-.79527 -.60625 -.59845 .80116 0 0)" x="-119.49" y="-17.281" width="51.33" height="5.7404" rx=".56877" ry="1.7116" style="paint-order:markers fill stroke"/> 13 + <rect transform="matrix(-.79539 -.6061 -.5986 .80105 0 0)" x="-116.87" y="-10.047" width="42.871" height="4.7942" rx=".47504" ry="1.4295" style="paint-order:markers fill stroke"/> 14 + <rect transform="matrix(-.79418 -.60768 -.59701 .80224 0 0)" x="-127.08" y="-33.89" width="67.224" height="7.5215" rx=".74488" ry="2.2427" style="paint-order:markers fill stroke"/> 15 + <rect transform="matrix(.79708 -.60388 .60082 .79938 0 0)" x="-25.766" y="49.102" width="59.5" height="6.6488" rx=".6593" ry="1.9825" style="paint-order:markers fill stroke"/> 16 + <rect transform="matrix(.79527 -.60625 .59845 .80116 0 0)" x="-21.988" y="56.801" width="51.33" height="5.7404" rx=".56877" ry="1.7116" style="paint-order:markers fill stroke"/> 17 + <rect transform="matrix(.79539 -.6061 .5986 .80105 0 0)" x="-19.39" y="64.017" width="42.871" height="4.7942" rx=".47504" ry="1.4295" style="paint-order:markers fill stroke"/> 18 + <rect transform="matrix(.79418 -.60768 .59701 .80224 0 0)" x="-29.442" y="40.37" width="67.224" height="7.5215" rx=".74488" ry="2.2427" style="paint-order:markers fill stroke"/> 19 + </g> 20 + </g> 21 + </svg>
+6
backend-rs/frontend_assets/svg/red_cross.svg
··· 1 + <!-- 2 + ~ Copyright (c) 2024, MLC 'Strawmelonjuice' Bloeiman 3 + ~ 4 + ~ Licensed under the BSD 3-Clause License. See the LICENSE file for more info. 5 + --> 6 + <svg xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" style="display:block;shape-rendering:auto" id="svg12" width="10" height="10" preserveAspectRatio="xMidYMid" version="1.1" viewBox="0 0 5 5"><ellipse style="display:block;fill:#c4c4c4;fill-opacity:.348548;stroke:none;stroke-width:.6375;stroke-dasharray:none;shape-rendering:auto" id="path2" cx="2.476" cy="2.538" rx="2.483" ry="2.497"/><path style="fill:#ff2a2a;stroke-width:.444338" id="path1" d="m 2.588645,1.9822203 c 0.024583,-0.016861 0.047324,-0.036989 0.073751,-0.050582 0.039331,-0.020231 0.093979,-0.042019 0.1352102,-0.063227 0.030274,-0.015572 -0.011097,0.036706 0.036876,-0.012645 0.063613,-0.065443 -0.1015306,0.026935 0.073751,-0.063227 0.066262,-0.034085 0.1310751,-0.051028 0.1966694,-0.088518 0.00702,-0.00401 0.016644,-0.04689 0.024583,-0.063228 0.012414,-0.025543 0.044391,-0.033023 0.061459,-0.050582 0.039322,-0.040454 0.00962,-0.028148 0.036876,-0.050582 0.027212,-0.022395 0.088166,-0.065411 0.1106266,-0.088518 0.039226,-0.040355 0.068414,-0.1336104 0.098335,-0.1643914 0.033201,-0.034157 0.048957,-0.029218 0.073751,-0.063227 0.00777,-0.010664 0.00313,-0.028511 0.012292,-0.037937 0.00647,-0.00666 0.018105,-0.00598 0.024584,-0.012645 0.00647,-0.00666 0.00721,-0.017449 0.012292,-0.025291 0.019551,-0.030171 0.057846,-0.041471 0.086043,-0.063227 0.048656,-0.037542 7.842e-4,-0.013454 0.049168,-0.063227 0.00647,-0.00667 0.018105,-0.00598 0.024584,-0.012645 0.00289,-0.00298 -0.0029,-0.009665 0,-0.0126453 0.00289,-0.002979 0.00939,0.002979 0.012292,0 0.0029,-0.002979 -0.0029,-0.009665 0,-0.0126453 0.018591,-0.0191262 0.057222,-0.037721 0.073751,-0.0632274 0.00694,-0.0107114 -0.00848,-0.0418542 0,-0.0505819 0.011768,-0.0121058 0.030824,0.0101553 0.036876,0.0126453 0.015685,0.006455 0.033483,0.006192 0.049168,0.0126453 0.018809,0.00774 0.177731,0.15527439 0.1843775,0.16439139 0.030692,0.0421 0.0074,0.1366685 0.024584,0.1896822 0.00366,0.01131 0.017344,0.015981 0.024583,0.025291 0.012798,0.016457 0.021137,0.03709 0.036876,0.050582 0.00995,0.00854 0.025287,0.00669 0.036876,0.012645 0.051156,0.026314 -0.01314,-8.743e-4 0.012292,0.025291 0.0029,0.00298 0.00939,-0.00297 0.012292,0 0.014516,0.014935 -0.00829,0.076164 -0.012292,0.088518 -0.00875,0.027021 -0.054294,0.077631 -0.086043,0.088518 -0.011661,0.00399 -0.025882,-0.00565 -0.036876,0 -0.013213,0.0068 -0.026429,0.014544 -0.036876,0.025291 -0.00289,0.00297 0.0029,0.00966 0,0.012645 -0.035218,0.036231 -0.098407,0.038012 -0.1352102,0.075873 -0.012292,0.012645 -0.026445,0.023629 -0.036876,0.037936 -0.01437,0.019711 -0.013719,0.066164 -0.024584,0.088518 -0.011089,0.022815 -0.03839,0.041052 -0.049168,0.063227 -0.00183,0.00377 0.00366,0.010761 0,0.012645 -0.00733,0.00378 -0.017254,-0.00377 -0.024584,0 -0.112867,0.058057 0.084282,-0.00939 -0.061459,0.050582 -0.00761,0.00314 -0.017254,-0.00377 -0.024584,0 -0.00367,0.00188 0.00183,0.00888 0,0.012645 -0.00777,0.015995 -0.026445,0.023629 -0.036876,0.037937 -0.00777,0.010663 -0.00313,0.028511 -0.012292,0.037937 -0.0029,0.00297 -0.00939,-0.00298 -0.012292,0 -0.0029,0.00297 0.00183,0.00888 0,0.012645 -0.00493,0.01015 -0.047983,0.062009 -0.049168,0.063227 -0.00647,0.00666 -0.017254,0.00699 -0.024584,0.012645 -0.013907,0.01073 -0.022412,0.028016 -0.036876,0.037937 -0.037762,0.025899 -0.086259,0.044371 -0.1229184,0.063227 -0.021433,0.011025 -0.041125,0.060184 -0.061459,0.075873 -0.00655,0.00506 -0.016809,-0.00267 -0.024583,0 -0.010994,0.00378 -0.014218,0.019959 -0.024584,0.025292 -0.00733,0.00377 -0.017254,-0.00378 -0.024583,0 -0.040324,0.020741 -0.019842,0.01795 -0.098335,0.025291 -0.018689,0.00175 -0.1013298,-0.00956 -0.1106266,0 -0.00289,0.00298 0.0029,0.00966 0,0.012645 -0.00289,0.00297 -0.00939,-0.00298 -0.012292,0 -0.0029,0.00297 0.00183,0.00888 0,0.012645 -0.00984,0.020237 -0.076955,0.057728 -0.098335,0.063227 -0.018194,0.00468 -0.045092,0.00423 -0.061459,0.012645 -0.020731,0.010664 0.032778,0.033721 0.049168,0.050582 0.014972,0.015403 0.00576,0.028253 0.024584,0.037937 0.00733,0.00378 0.018789,-0.00596 0.024583,0 0.0029,0.00298 -0.0029,0.00966 0,0.012645 0.025432,0.026164 -9.989e-4,-0.039982 0.024584,0.012645 0.010879,0.022385 -0.018284,0.00433 0.012292,0.025291 0.023428,0.016068 0.053535,0.029785 0.073751,0.050582 0.00647,0.00666 0.00581,0.018627 0.012292,0.025292 0.00647,0.00666 0.018106,0.00598 0.024584,0.012645 0.048382,0.049774 5.126e-4,0.025686 0.049168,0.063227 0.013133,0.010133 0.078735,0.051951 0.086043,0.063228 0.023286,-0.00144 0.00819,0.029506 0.012292,0.037937 0.0041,0.00843 0.020486,0.00422 0.024584,0.012645 0.00366,0.00754 0,0.016861 0,0.025291 0.00819,0.00843 0.018621,0.015067 0.024583,0.025292 0.029656,0.015266 0.00295,0.044003 0.012292,0.063227 0.045644,0.093913 0.1085448,0.1650372 0.1597939,0.2529098 0.022295,0.038226 0.035407,0.076288 0.061459,0.1138094 0.08353,0.1203058 -0.046884,-0.088744 0.061459,0.050582 0.021249,0.027327 0.043591,0.1115268 0.061459,0.1391004 0.014363,0.022165 0.065879,0.064226 0.073751,0.088518 0.00259,0.008 -0.00259,0.017293 0,0.025292 0.00924,0.028512 0.057309,0.0505 0.073751,0.075873 0.031998,0.049378 -0.013913,5.273e-4 0.036876,0.063228 0.023739,0.029305 0.055523,0.054941 0.086043,0.075873 0.054745,0.037547 0.097763,0.1029273 0.073751,0.1770368 -0.00579,0.017884 -0.011627,0.037253 -0.024584,0.050582 -0.00782,0.00805 -0.033476,0.017219 -0.049168,0.025292 -0.028761,0.014794 -0.054828,0.052523 -0.086043,0.063227 -0.00777,0.00267 -0.017052,-0.00332 -0.024584,0 -0.038773,0.017095 -0.072896,0.043819 -0.1106266,0.063228 -0.00819,0.00421 -0.015892,0.00966 -0.024583,0.012645 -0.00777,0.00267 -0.018789,-0.00596 -0.024584,0 -0.025432,0.026164 0.038864,-0.00102 -0.012292,0.025291 -0.044184,0.022727 -0.080246,0.061737 -0.1352102,0.075873 -0.011925,0.00306 -0.024951,-0.00306 -0.036876,0 -0.00563,0.00145 -0.00711,0.00998 -0.012292,0.012645 -0.035044,0.018024 -0.031715,-0.00937 -0.036874,-0.025295 -0.00183,-0.00566 -0.01046,-0.00699 -0.012292,-0.012645 -0.00259,-0.008 0.00366,-0.017751 0,-0.025291 -0.013319,-0.027399 -0.069646,-0.059 -0.086044,-0.07587 -0.014952,-0.015382 -0.055233,-0.1074042 -0.061459,-0.1138094 -0.00289,-0.00298 -0.00939,0.00297 -0.012292,0 -0.0029,-0.00298 0.0029,-0.00966 0,-0.012645 C 3.2700976,4.049 3.2582446,4.04881 3.2524046,4.030789 3.2418846,3.998321 3.2612846,4.000233 3.2401126,3.967562 3.2090576,3.919641 3.0109646,3.7535137 3.006568,3.7399443 c -0.00647,-0.019994 0.00402,-0.042561 0,-0.063227 -0.00989,-0.050887 -0.00897,-0.00581 -0.024583,-0.037937 -0.00367,-0.00754 0.00367,-0.017751 0,-0.025292 -0.025578,-0.052627 8.498e-4,0.013519 -0.024584,-0.012645 -0.037728,-0.038814 0.00718,-0.010515 -0.012292,-0.050582 -0.00183,-0.00378 -0.00939,0.00298 -0.012292,0 -0.00515,-0.0053 -0.00857,-0.030273 -0.012292,-0.037937 C 2.904647,3.4796563 2.86456,3.4558263 2.846774,3.4238063 2.834482,3.3985153 2.822191,3.3732243 2.809898,3.3479333 2.801708,3.3395033 2.791277,3.3328663 2.785315,3.3226423 2.764213,3.2864603 2.768143,3.1407743 2.760732,3.0950235 2.739328,2.962907 2.763872,3.0856035 2.736149,3.0570865 2.732969,3.0538165 2.713099,2.9495885 2.711566,2.9432771 2.701378,2.9013521 2.667917,2.8661121 2.650107,2.8294677 c -0.010638,-0.02189 -0.0043,-0.055008 -0.024584,-0.075873 -0.00289,-0.00298 -0.01046,0.00378 -0.012292,0 -0.00367,-0.00754 0.00455,-0.018277 0,-0.025292 -0.029942,-0.046206 -0.017994,0.013559 -0.036876,-0.025291 -0.00848,-0.017442 0.014886,-0.01131 -0.012292,-0.025292 -0.020804,-0.010701 -0.06594,0.072446 -0.073751,0.088518 -0.00367,0.00754 0.00367,0.017751 0,0.025292 -0.00787,0.016191 -0.01912,0.033721 -0.024584,0.050582 -0.00388,0.011997 0.00869,0.028994 0,0.037937 -0.00647,0.00666 -0.018106,0.00599 -0.024583,0.012645 -0.0029,0.00298 0.00227,0.00914 0,0.012645 -0.015462,0.02386 -0.051421,0.058644 -0.073751,0.075873 -0.00733,0.00565 -0.018105,0.00599 -0.024583,0.012645 -0.00571,0.00588 -0.019663,0.040463 -0.036876,0.050582 -0.015003,0.00882 -0.08956,0.041554 -0.1106265,0.063227 -0.010446,0.010746 -0.014138,0.02719 -0.024584,0.037937 -0.00289,0.00298 -0.00973,-0.00329 -0.012292,0 -0.014925,0.019192 -0.01822,0.047874 -0.036876,0.063227 -0.024366,0.020054 -0.059582,0.020923 -0.086043,0.037937 -0.011704,0.00753 -0.019355,0.039824 -0.024583,0.050582 -0.024132,0.049652 -0.09477,0.082606 -0.1352102,0.1138094 -0.022711,0.017524 -0.091043,0.111309 -0.098335,0.1138095 -0.00777,0.00267 -0.01681,-0.00267 -0.024583,0 -0.046077,0.015801 -0.065427,0.091107 -0.086043,0.1264549 -0.01982,0.033983 -0.056264,0.065184 -0.073751,0.1011639 -0.00183,0.00377 0.0029,0.00966 0,0.012645 -0.00289,0.00298 -0.00939,-0.00298 -0.012292,0 -0.0029,0.00298 0.00183,0.00888 0,0.012645 -0.00863,0.017757 -0.043934,0.032553 -0.049168,0.037937 -0.00647,0.00666 -0.00721,0.017449 -0.012292,0.025291 -0.022512,0.034741 -0.06138,0.050422 -0.073751,0.075874 -0.020848,0.042896 -0.079956,0.056966 -0.1106267,0.088518 -0.028427,0.029245 -0.06721,0.123497 -0.1106265,0.1011639 -0.028295,-0.014554 0.00536,-0.014833 -0.024583,-0.037936 -0.020731,-0.015996 -0.051531,-0.011575 -0.073751,-0.025291 -0.019875,-0.012268 -0.028437,-0.039918 -0.049167,-0.050582 -0.071947,-0.037009 -0.026523,0.0071 -0.073751,-0.025291 -0.0269216,-0.018464 -0.0447372,-0.060949 -0.073751,-0.075873 -0.003667,-0.00188 -0.0104596,0.00377 -0.0122917,0 -0.003667,-0.00754 0.002593,-0.017293 0,-0.025292 -0.0127889,-0.03947 -0.008195,0.00422 -0.0245838,-0.012645 -0.002896,-0.00298 0.002274,-0.00914 0,-0.012645 -0.0150818,-0.023274 -0.0352081,-0.030756 -0.0491673,-0.037937 -0.0183236,-0.00943 -0.0223895,-0.035679 -0.0368755,-0.050582 -0.003286,-0.00338 -0.0662452,-0.033013 -0.0491673,-0.050582 0.0462463,-0.047577 0.12805774,-0.031499 0.18437752,-0.037937 0.0148159,-0.00169 0.10289445,-0.059964 0.11062645,-0.075873 0.012321,-0.025351 -0.020322,-1.271e-4 0.012292,-0.025291 0.034501,-0.02662 0.087161,-0.048285 0.1106265,-0.088518 0.00667,-0.01143 0.0065,-0.026013 0.012292,-0.037937 -0.00262,-0.02629 0.019086,-0.0051 0.024583,-0.012645 0.010994,-0.015081 0.01442,-0.034897 0.024583,-0.050582 0.017409,-0.026866 0.044024,-0.048968 0.061459,-0.075873 0.00499,-0.00771 0.019592,-0.042877 0.024583,-0.050582 0.048391,-0.074676 0.1332565,-0.11956 0.1843774,-0.1896823 0.01536,-0.021069 0.012962,-0.057939 0.024584,-0.075873 0.00643,-0.00992 0.019401,-0.014627 0.024583,-0.025291 0.00721,-0.014835 0.00436,-0.046917 0.012292,-0.063227 0.00483,-0.00994 0.068599,-0.095864 0.073751,-0.101164 0.022628,-0.023279 0.04815,-0.043474 0.073751,-0.063227 0.010366,-0.008 0.027714,-0.00322 0.036876,-0.012645 0.01639,-0.01686 -0.026075,-0.012134 0.012292,-0.025291 0.00777,-0.00267 0.018789,0.00596 0.024583,0 0.00579,-0.00596 -0.00455,-0.018277 0,-0.025292 0.016566,-0.025563 0.095562,-0.047728 0.1229183,-0.075873 0.030129,-0.030995 0.011953,-0.050232 0.036876,-0.075873 0.00614,-0.00632 0.018438,-0.00632 0.024583,-0.012645 0.0029,-0.00298 -0.00289,-0.00966 0,-0.012645 0.0041,-0.00422 0.032778,-0.00843 0.036876,-0.012645 0.0029,-0.00298 -0.00341,-0.010307 0,-0.012645 0.044667,-0.030635 0.1844223,-0.012692 0.2089611,-0.037937 0.02093,-0.021532 -0.028585,-0.046318 -0.036876,-0.050582 -0.02159,-0.011105 -0.2502661,-0.114834 -0.2581286,-0.1391004 -0.00259,-0.008 0.00366,-0.017751 0,-0.025292 -0.00183,-0.00378 -0.00939,0.00298 -0.012292,0 -0.00289,-0.00298 0.00183,-0.00888 0,-0.012645 C 1.9059826,2.183598 1.8972376,2.171122 1.8880096,2.159255 1.8771506,2.14529 1.8589086,2.137314 1.8511336,2.121318 c -0.00183,-0.00378 0.0029,-0.00966 0,-0.012645 -0.00515,-0.0053 -0.029426,-0.00882 -0.036875,-0.012645 -0.015548,-0.008 -0.022969,-0.027207 -0.036876,-0.037937 -0.031015,-0.023931 -0.076572,-0.025913 -0.098335,-0.063228 -0.00667,-0.01143 -0.00511,-0.026846 -0.012292,-0.037937 -0.00227,-0.00351 -0.00819,0 -0.012292,0 -0.00409,-0.00421 -0.0097,-0.00731 -0.012292,-0.012645 C 1.6328416,1.925092 1.6308526,1.901523 1.6175876,1.881054 1.5997116,1.853468 1.5536686,1.835527 1.5438366,1.805181 c -0.00259,-0.008 0.00259,-0.017294 0,-0.025291 C 1.5433589,1.77841 1.4875416,1.698455 1.4823776,1.691372 1.4791576,1.686942 1.4249436,1.61964 1.4209186,1.615499 1.3980186,1.59194 1.3508026,1.572397 1.3348756,1.539626 1.2789196,1.4244943 1.3629996,1.555913 1.2979996,1.489044 1.2915296,1.482384 1.2921796,1.470418 1.2857076,1.463753 1.2792376,1.457093 1.2676026,1.457763 1.2611246,1.451108 1.2552546,1.445068 1.2429416,1.409305 1.2365416,1.400526 1.1702296,1.309567 1.2501906,1.427213 1.1873736,1.362589 1.1575196,1.331876 1.1910236,1.34402 1.1627906,1.324652 1.1551706,1.319422 1.1446856,1.318662 1.1382076,1.312007 1.1144574,1.287574 1.1004984,1.247921 1.0767485,1.223489 c -0.0029,-0.00298 -0.00863,0.00188 -0.012292,0 -0.051156,-0.026314 0.01314,8.743e-4 -0.012292,-0.025291 -0.025432,-0.026164 9.945e-4,0.039982 -0.024584,-0.012645 -0.00183,-0.00377 0.0029,-0.00966 0,-0.012645 -0.0029,-0.00298 -0.00863,0.00188 -0.012292,0 -0.010365,-0.00534 -0.014218,-0.019959 -0.0245838,-0.025291 -0.00733,-0.00378 -0.0187893,0.00596 -0.0245838,0 -0.002896,-0.00297 0,-0.00843 0,-0.012645 0,-0.039942 0.0183801,-0.01682 0.0491673,-0.037936 0.00762,-0.00523 0.0041,-0.021076 0.012292,-0.025291 0.018755,-0.00965 0.1130412,-0.023386 0.1229185,-0.025291 0.2155432,-0.041577 0.082499,-0.018774 0.1597938,-0.050582 0.035425,-0.0145779 0.077813,-0.008412 0.1106266,-0.0252911 0.017926,-0.009221 0.019664,-0.0202295 0.024583,-0.0252911 0.012292,-0.0126453 0.049168,0 0.061459,-0.0126453 0.00897,-0.00923 0.025828,-0.0689105 0.036876,-0.0632274 0.018826,0.009684 0.00961,0.0225347 0.024583,0.0379364 0.0066,0.00679 0.094854,0.0768248 0.1106265,0.10116389 0.015246,0.023527 0.019894,0.053639 0.036876,0.075873 0.047859,0.062665 0.1006457,0.1061086 0.1597938,0.151746 0.037651,0.029051 -0.020154,-0.00809 0.024583,0.037936 0.017933,0.018449 0.1123921,0.068676 0.1229185,0.1011639 0.00518,0.015995 -0.00398,0.034224 0,0.050582 0.016687,0.068666 0.071311,0.1245724 0.1229183,0.1643914 0.00733,0.00565 0.018106,0.00598 0.024583,0.012645 0.012383,0.012739 0.016732,0.038466 0.024584,0.050582 0.011364,0.017536 0.022389,0.035679 0.036876,0.050582 0.00647,0.00666 0.019502,0.0048 0.024583,0.012645 0.01216,0.018765 -2.993e-4,0.024983 0.012292,0.037937 0.00647,0.00666 0.019502,0.0048 0.024583,0.012645 0.00455,0.00701 -0.00367,0.017751 0,0.025291 0.012292,0.025292 0.024584,0.018968 0.036876,0.037937 0.019982,0.030834 0.060173,0.073227 0.073751,0.1011639 0.00183,0.00378 -0.0029,0.00966 0,0.012645 0.0029,0.00297 0.00863,-0.00188 0.012292,0 0.011879,0.00611 0.043227,0.038361 0.049168,0.050582 0.00183,0.00378 -0.0029,0.00967 0,0.012645 0.00579,0.00596 0.018789,-0.00596 0.024583,0 0.0029,0.00298 -0.0029,0.00967 0,0.012645 0.00244,0.0025 0.045802,0.00207 0.049167,0 0.017567,-0.010844 0.032778,-0.025291 0.049168,-0.037937 z"/></svg>
+6
backend-rs/frontend_assets/svg/spinner.svg
··· 1 + <!-- 2 + ~ Copyright (c) 2024, MLC 'Strawmelonjuice' Bloeiman 3 + ~ 4 + ~ Licensed under the BSD 3-Clause License. See the LICENSE file for more info. 5 + --> 6 + <svg xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" style="display:block;shape-rendering:auto" id="svg12" width="10" height="10" preserveAspectRatio="xMidYMid" version="1.1" viewBox="0 0 5 5"><ellipse style="display:block;shape-rendering:auto;fill:#928e85;fill-opacity:.348548;stroke:none;stroke-width:.6375;stroke-dasharray:none" id="path2" cx="2.485" cy="2.5" rx="2.483" ry="2.497"/><g id="g1" transform="matrix(0.08486573,0,0,0.08243763,-1.689977,-1.6176723)"><rect id="rect1" width="6" height="12" x="47" y="24" fill="#fe718d" rx="3" ry="6"><animate attributeName="opacity" begin="-0.9166666666666666s" dur="1s" keyTimes="0;1" repeatCount="indefinite" values="1;0"/></rect></g><g id="g2" transform="matrix(0.07349589,0.04121881,-0.04243287,0.07139307,1.0001591,-3.1263857)"><rect id="rect2" width="6" height="12" x="47" y="24" fill="#fe718d" rx="3" ry="6"><animate attributeName="opacity" begin="-0.8333333333333334s" dur="1s" keyTimes="0;1" repeatCount="indefinite" values="1;0"/></rect></g><g id="g3" transform="matrix(0.04243287,0.07139307,-0.07349589,0.04121881,4.1064607,-3.1263857)"><rect id="rect3" width="6" height="12" x="47" y="24" fill="#fe718d" rx="3" ry="6"><animate attributeName="opacity" begin="-0.75s" dur="1s" keyTimes="0;1" repeatCount="indefinite" values="1;0"/></rect></g><g id="g4" transform="matrix(0,0.08243763,-0.08486573,0,6.7965963,-1.6176723)"><rect id="rect4" width="6" height="12" x="47" y="24" fill="#fe718d" rx="3" ry="6"><animate attributeName="opacity" begin="-0.6666666666666666s" dur="1s" keyTimes="0;1" repeatCount="indefinite" values="1;0"/></rect></g><g id="g5" transform="matrix(-0.04243287,0.07139307,-0.07349589,-0.04121881,8.3497475,0.99549531)"><rect id="rect5" width="6" height="12" x="47" y="24" fill="#fe718d" rx="3" ry="6"><animate attributeName="opacity" begin="-0.5833333333333334s" dur="1s" keyTimes="0;1" repeatCount="indefinite" values="1;0"/></rect></g><g id="g6" transform="matrix(-0.07349589,0.04121881,-0.04243287,-0.07139307,8.3497475,4.0129221)"><rect id="rect6" width="6" height="12" x="47" y="24" fill="#fe718d" rx="3" ry="6"><animate attributeName="opacity" begin="-0.5s" dur="1s" keyTimes="0;1" repeatCount="indefinite" values="1;0"/></rect></g><g id="g7" transform="matrix(-0.08486573,0,0,-0.08243763,6.7965963,6.62609)"><rect id="rect7" width="6" height="12" x="47" y="24" fill="#fe718d" rx="3" ry="6"><animate attributeName="opacity" begin="-0.4166666666666667s" dur="1s" keyTimes="0;1" repeatCount="indefinite" values="1;0"/></rect></g><g id="g8" transform="matrix(-0.07349589,-0.04121881,0.04243287,-0.07139307,4.1064607,8.1348033)"><rect id="rect8" width="6" height="12" x="47" y="24" fill="#fe718d" rx="3" ry="6"><animate attributeName="opacity" begin="-0.3333333333333333s" dur="1s" keyTimes="0;1" repeatCount="indefinite" values="1;0"/></rect></g><g id="g9" transform="matrix(-0.04243287,-0.07139307,0.07349589,-0.04121881,1.0001591,8.1348033)"><rect id="rect9" width="6" height="12" x="47" y="24" fill="#fe718d" rx="3" ry="6"><animate attributeName="opacity" begin="-0.25s" dur="1s" keyTimes="0;1" repeatCount="indefinite" values="1;0"/></rect></g><g id="g10" transform="matrix(0,-0.08243763,0.08486573,0,-1.689977,6.62609)"><rect id="rect10" width="6" height="12" x="47" y="24" fill="#fe718d" rx="3" ry="6"><animate attributeName="opacity" begin="-0.16666666666666666s" dur="1s" keyTimes="0;1" repeatCount="indefinite" values="1;0"/></rect></g><g id="g11" transform="matrix(0.04243287,-0.07139307,0.07349589,0.04121881,-3.2431278,4.0129221)"><rect id="rect11" width="6" height="12" x="47" y="24" fill="#fe718d" rx="3" ry="6"><animate attributeName="opacity" begin="-0.08333333333333333s" dur="1s" keyTimes="0;1" repeatCount="indefinite" values="1;0"/></rect></g><g id="g12" transform="matrix(0.07349589,-0.04121881,0.04243287,0.07139307,-3.2431278,0.99549531)"><rect id="rect12" width="6" height="12" x="47" y="24" fill="#fe718d" rx="3" ry="6"><animate attributeName="opacity" begin="0s" dur="1s" keyTimes="0;1" repeatCount="indefinite" values="1;0"/></rect></g></svg>
+39
backend-rs/frontend_assets/toml/initial_config.toml
··· 1 + version = "0.3-a" 2 + [server] 3 + # What port to bind to (the server is designed with apache2 reverse-proxy in mind, so 80 is not necessarily default.) 4 + port = 8085 5 + # What adress to bind to? 6 + adress = "127.0.0.1" 7 + # Secret key for cookie encryption 8 + cookiekey = "Uneaten email in the washing machines to dry the popcorn" 9 + # For development purposes, please set this to true on prod (and use SSL!) 10 + secure = true 11 + 12 + [interinstance] 13 + # Instance ID, equals, the domain name this instance is open on. 14 + iid = "example.com" 15 + # Specifies instances to send sync requests to. Note that these are only answered if both servers have each other listed. If not, the admin's will get a request to add them, but don't necessarily have to. 16 + synclist = [ 17 + # Of course, by default, the home domain is included, however! You can just remove it if you want to! 18 + { name = "peonies.xyz", level = "full", key = "A secret kept between two friends (servers :) Do not re-use these keys!)" }, 19 + ] 20 + # Ignored instances are no longer allowed to send requests to join this instance's synclist. 21 + ignorelist = ["example.com"] 22 + [interinstance.syncing] 23 + # Specifies the interval between syncs. Minimum is 30. 24 + syncintervall = 120 25 + 26 + [database] 27 + # What kind of database to use, currently only supporting "sqlite". 28 + method = "sqlite" 29 + # Encryption key for sensitive data like passwords. Changing it will immediately mark all existing passwords as false. 30 + cryptkey = "In these parts, passwords are encrypted. Pardners. How do you do?" 31 + 32 + [database.sqlite] 33 + # The database file to use for sqlite. 34 + file = "instance-db.sqlite" 35 + 36 + [logging] 37 + file-loglevel = 3 38 + console-loglevel = 2 39 + file = "instance-logging.log"
+15
backend-rs/libs/lumina-urls/Cargo.toml
··· 1 + [package] 2 + name = "lumina-urls" 3 + description = "Parses urls and returns their information, used in Lumina for embeds" 4 + authors.workspace = true 5 + license.workspace = true 6 + repository.workspace = true 7 + edition.workspace = true 8 + version = "0.1.0" 9 + 10 + [dependencies] 11 + scraper = "0.19.0" 12 + regex = { workspace = true } 13 + serde = {workspace = true} 14 + reqwest = { workspace = true, features = ["blocking"] } 15 + serde_json= {workspace = true}
+100
backend-rs/libs/lumina-urls/src/lib.rs
··· 1 + /* 2 + * Copyright (c) 2024, MLC 'Strawmelonjuice' Bloeiman 3 + * 4 + * Licensed under the BSD 3-Clause License. See the LICENSE file for more info. 5 + */ 6 + 7 + use crate::PageType::General; 8 + use crate::UrlMetaInfo::{Fail, Succes}; 9 + use scraper::{Html, Selector}; 10 + 11 + pub enum PageType { 12 + General, 13 + Article { 14 + summary: Option<String>, 15 + author: Option<String>, 16 + published_time: Option<String>, 17 + modified_time: Option<String>, 18 + expiration_time: Option<String>, 19 + section: Option<String>, 20 + tag: Option<String>, 21 + }, 22 + Media { 23 + mime_type: Option<String>, 24 + media_url: Option<String>, 25 + }, 26 + } 27 + pub struct UrlMetaTags { 28 + pub title: String, 29 + pub description: String, 30 + pub thumbnail: Option<String>, 31 + pub page: PageType, 32 + } 33 + pub enum UrlMetaInfo { 34 + Succes(Box<UrlMetaTags>), 35 + Fail, 36 + Unset, 37 + } 38 + 39 + impl UrlMetaInfo { 40 + pub fn get(mut self, url: String) -> UrlMetaInfo { 41 + match self { 42 + Succes(_) | Fail => self, 43 + UrlMetaInfo::Unset => { 44 + let html = match reqwest::blocking::get(url) { 45 + Ok(w) => match w.text() { 46 + Ok(o) => o, 47 + Err(_) => { 48 + self = Fail; 49 + return self; 50 + } 51 + }, 52 + Err(_) => { 53 + self = Fail; 54 + return self; 55 + } 56 + }; 57 + let document = Html::parse_document(&html); 58 + let title = onesome( 59 + { 60 + let selector = Selector::parse("title").unwrap(); 61 + document 62 + .select(&selector) 63 + .next() 64 + .map(|elm| String::from(elm.value().name())) 65 + }, 66 + extract("og:title", &document), 67 + ) 68 + .unwrap_or(String::from("Could not get a title at this time.")); 69 + self = Succes(Box::from(UrlMetaTags { 70 + title, 71 + description: onesome( 72 + extract("description", &document), 73 + extract("og:description", &document), 74 + ) 75 + .unwrap_or(String::from("Could not get a description at this time.")), 76 + thumbnail: onesome(extract("og:image", &document), extract("image", &document)), 77 + page: General, 78 + })); 79 + self 80 + } 81 + } 82 + } 83 + } 84 + 85 + fn extract(name: &str, document: &Html) -> Option<String> { 86 + let selector = Selector::parse(format!(r#"meta[name="{}"]"#, name).as_str()).unwrap(); 87 + match document.select(&selector).next() { 88 + Some(elm) => elm.value().attr("content").map(String::from), 89 + None => None, 90 + } 91 + } 92 + 93 + /// Out of two `Option<T>`'s, picks the one containing a `Some(T)`. 94 + /// If both have `Some`, `a` will be preferred. If both have `None`, `None` will be returned. 95 + fn onesome<T>(a: Option<T>, b: Option<T>) -> Option<T> { 96 + match a { 97 + Some(aa) => Some(aa), 98 + None => b, 99 + } 100 + }
+442
backend-rs/src/api_fe.rs
··· 1 + /* 2 + * Copyright (c) 2024, MLC 'Strawmelonjuice' Bloeiman 3 + * 4 + * Licensed under the BSD 3-Clause License. See the LICENSE file for more info. 5 + */ 6 + 7 + //! ## API's to the front-end. 8 + //! This module contains the API endpoints for the frontend, most of them being Actix request factories. 9 + 10 + use actix_session::Session; 11 + use actix_web::http::header::{CacheControl, CacheDirective}; 12 + use actix_web::http::StatusCode; 13 + use actix_web::web::Data; 14 + use actix_web::{HttpRequest, HttpResponse}; 15 + use colored::Colorize; 16 + use serde::{Deserialize, Serialize}; 17 + use tokio::sync::{Mutex, MutexGuard}; 18 + 19 + use crate::database::users::auth::{check, AuthResponse}; 20 + use crate::database::users::{add, SafeUser}; 21 + use crate::database::{self}; 22 + use crate::{LuminaConfig, ServerVars}; 23 + 24 + #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 25 + #[serde(rename_all = "snake_case")] 26 + struct FEJSonObj { 27 + // pub pulled: i128, 28 + pub instance: FEJsonObjInstanceInfo, 29 + pub user: SafeUser, 30 + } 31 + 32 + #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 33 + #[serde(rename_all = "snake_case")] 34 + struct FEJsonObjInstanceInfo { 35 + pub iid: String, 36 + pub last_sync: i64, 37 + } 38 + 39 + pub enum ShieldValue { 40 + Notsafe(HttpResponse), 41 + Safe, 42 + } 43 + async fn shield( 44 + session: Session, 45 + server_vars_mutex: &Data<Mutex<ServerVars>>, 46 + halt: HttpResponse, 47 + ) -> ShieldValue { 48 + let server_vars = ServerVars::grab(server_vars_mutex).await; 49 + let config = server_vars.clone().config; 50 + let id_ = session.get::<i64>("userid").unwrap_or(None); 51 + let id = id_.unwrap_or(-100); 52 + let safe = checksessionvalidity(id, &session, &config); 53 + if !safe { 54 + session.purge(); 55 + ShieldValue::Notsafe(halt) 56 + } else { 57 + ShieldValue::Safe 58 + } 59 + } 60 + 61 + pub(crate) fn checksessionvalidity(id: i64, session: &Session, config: &LuminaConfig) -> bool { 62 + match id { 63 + -100 => false, 64 + _ => match session.get::<i64>("validity") { 65 + Ok(s) => matches!(s, Some(a) if a == config.clone().erun.session_valid), 66 + Err(_) => false, 67 + }, 68 + } 69 + } 70 + 71 + pub(crate) async fn update( 72 + server_vars_mutex: Data<Mutex<ServerVars>>, 73 + session: Session, 74 + req: HttpRequest, 75 + ) -> HttpResponse { 76 + let server_vars: ServerVars = ServerVars::grab(&server_vars_mutex).await; 77 + let config: LuminaConfig = server_vars.clone().config; 78 + let coninfo = req.connection_info(); 79 + let ip = coninfo.realip_remote_addr().unwrap_or("<unknown IP>"); 80 + let username_a = session.get::<String>("username"); 81 + let username_b = username_a.unwrap_or(None).unwrap_or(String::from("")); 82 + let username_c = if username_b != *"" { 83 + format!("/{}", username_b.green()) 84 + } else { 85 + String::from("") 86 + }; 87 + info!( 88 + "{}\t{:>45.47}\t\t{}{:<26}", 89 + "Request/200".bright_green(), 90 + "/api/fe/update".magenta(), 91 + ip.yellow(), 92 + username_c 93 + ); 94 + let mut d: FEJSonObj = FEJSonObj { 95 + // pulled: -1, 96 + instance: FEJsonObjInstanceInfo { 97 + iid: config.lumina_synchronisation_iid.clone(), 98 + last_sync: -1, 99 + }, 100 + user: SafeUser { 101 + username: "unset".to_string(), 102 + id: -1, 103 + email: "unset".to_string(), 104 + }, 105 + }; 106 + let userd_maybe = database::fetch::user(&config, ("username", username_b)).unwrap_or(None); 107 + if let Some(userd) = userd_maybe { 108 + d.user = SafeUser { 109 + username: userd.username, 110 + id: userd.id, 111 + email: userd.email, 112 + }; 113 + }; 114 + return HttpResponse::build(StatusCode::OK) 115 + .insert_header(CacheControl(vec![CacheDirective::NoCache])) 116 + .content_type("text/json; charset=utf-8") 117 + .body(serde_json::to_string(&d).unwrap()); 118 + } 119 + #[derive(Deserialize)] 120 + pub(super) struct AuthReqData { 121 + username: String, 122 + password: String, 123 + } 124 + 125 + pub(crate) async fn auth( 126 + server_vars_mutex: Data<Mutex<ServerVars>>, 127 + session: Session, 128 + req: HttpRequest, 129 + data: actix_web::web::Json<AuthReqData>, 130 + ) -> HttpResponse { 131 + let server_vars = ServerVars::grab(&server_vars_mutex).await; 132 + let config = server_vars.clone().config; 133 + server_vars.tell("Auth request received."); 134 + let result = check( 135 + data.username.clone(), 136 + data.password.clone(), 137 + &server_vars_mutex, 138 + ) 139 + .await; 140 + let coninfo = req.connection_info(); 141 + let ip = coninfo.realip_remote_addr().unwrap_or("<unknown IP>"); 142 + match result { 143 + AuthResponse::Success(user_id) => { 144 + let user = database::fetch::user(&config, ("id", user_id.to_string())) 145 + .unwrap() 146 + .unwrap(); 147 + let username = user.username; 148 + info!("User '{0}' logged in succesfully from {1}", username, ip); 149 + session.insert("userid", user.id).unwrap(); 150 + session.insert("username", username).unwrap(); 151 + session 152 + .insert("validity", config.clone().erun.session_valid) 153 + .unwrap(); 154 + HttpResponse::build(StatusCode::OK) 155 + .insert_header(CacheControl(vec![CacheDirective::NoCache])) 156 + .content_type("text/json; charset=utf-8") 157 + .body(r#"{"Ok": true, "Errorvalue": ""}"#) 158 + } 159 + _ => HttpResponse::build(StatusCode::UNAUTHORIZED) 160 + .insert_header(CacheControl(vec![CacheDirective::NoCache])) 161 + .content_type("text/json; charset=utf-8") 162 + .body(r#"{"Ok": false}"#), 163 + } 164 + } 165 + 166 + #[derive(Deserialize)] 167 + pub(super) struct AuthCreateUserReqData { 168 + username: String, 169 + email: String, 170 + password: String, 171 + } 172 + pub(crate) async fn newaccount( 173 + server_vars_mutex: Data<Mutex<ServerVars>>, 174 + session: Session, 175 + req: HttpRequest, 176 + data: actix_web::web::Json<AuthCreateUserReqData>, 177 + ) -> HttpResponse { 178 + let server_vars = ServerVars::grab(&server_vars_mutex).await; 179 + let config = server_vars.clone().config; 180 + server_vars.tell("User creation request: received."); 181 + let result = add( 182 + data.username.clone(), 183 + data.email.clone(), 184 + data.password.clone(), 185 + &config.clone(), 186 + ); 187 + let coninfo = req.connection_info(); 188 + let ip = coninfo.realip_remote_addr().unwrap_or("<unknown IP>"); 189 + match result { 190 + Ok(user_id) => { 191 + let user = database::fetch::user(&config, ("id", user_id.to_string())) 192 + .unwrap() 193 + .unwrap(); 194 + let username = user.username; 195 + session.insert("userid", user.id).unwrap(); 196 + session.insert("username", username).unwrap(); 197 + session 198 + .insert("validity", config.clone().erun.session_valid) 199 + .unwrap(); 200 + server_vars.tell(format!( 201 + "User creation request: approved for {} @ {}", 202 + user.id, ip 203 + )); 204 + HttpResponse::build(StatusCode::OK) 205 + .insert_header(CacheControl(vec![CacheDirective::NoCache])) 206 + .content_type("text/json; charset=utf-8") 207 + .body(r#"{"Ok": true}"#) 208 + } 209 + Err(e) => { 210 + server_vars.tell(format!("User creation request: denied - {e}")); 211 + HttpResponse::build(StatusCode::EXPECTATION_FAILED) 212 + .insert_header(CacheControl(vec![CacheDirective::NoCache])) 213 + .content_type("text/json; charset=utf-8") 214 + .body(format!(r#"{{"Ok": false, "Errorvalue": "{}"}}"#, e)) 215 + } 216 + } 217 + } 218 + #[derive(Deserialize)] 219 + pub struct FEPageServeRequest { 220 + location: String, 221 + } 222 + #[derive(Serialize)] 223 + struct FEPageServeResponse { 224 + main: String, 225 + side: String, 226 + /* 227 + * Message Numbers and Corresponding Messages: 228 + * 229 + * 1: "It seems your session has expired." 230 + * 2: "This page does not exist according to the instance server." 231 + * 33: "Notification centre - special page." 232 + * 34: Content from `STR_ASSETS_EDITOR_WINDOW_HTML` 233 + * 234 + * 9** messages notify the client to use certain templates to parse the data in. These are always 235 + * accompanied by a 899 code. 236 + * 901: "Homepage/timelines left+right" 237 + * 909: "Notification centre" 238 + */ 239 + message: Vec<i64>, 240 + } 241 + pub(crate) async fn pageservresponder( 242 + server_vars_mutex: Data<Mutex<ServerVars>>, 243 + session: Session, 244 + _req: HttpRequest, 245 + data: actix_web::web::Json<FEPageServeRequest>, 246 + ) -> HttpResponse { 247 + match shield( 248 + session.clone(), 249 + &server_vars_mutex, 250 + HttpResponse::build(StatusCode::OK) 251 + .insert_header(CacheControl(vec![CacheDirective::NoCache])) 252 + .content_type("text/json; charset=utf-8") 253 + .body( 254 + serde_json::to_string(&FEPageServeResponse { 255 + main: String::from("It seems your session has expired."), 256 + side: String::new(), 257 + message: vec![1], 258 + }) 259 + .unwrap(), 260 + ), 261 + ) 262 + .await 263 + { 264 + ShieldValue::Notsafe(o) => o, 265 + ShieldValue::Safe => { 266 + let config = { 267 + let server_vars: MutexGuard<ServerVars> = server_vars_mutex.lock().await; 268 + server_vars.clone().config.clone() 269 + }; 270 + // These three WILL be used in the future, when pages actually get dynamic. 271 + let location = data.location.clone(); 272 + let id_ = session.get::<i64>("userid").unwrap_or(Some(-100)); 273 + let id = id_.unwrap_or(-100); 274 + let user: database::users::User = 275 + database::fetch::user(&config, ("id", id.to_string())) 276 + .unwrap() 277 + .unwrap(); 278 + let server_vars = server_vars_mutex.lock().await.clone(); 279 + let config: LuminaConfig = server_vars.clone().config; 280 + let o: FEPageServeResponse = match location.as_str() { 281 + "home" => FEPageServeResponse { 282 + main: serde_json::to_string(&subpagedata::HomePageData { 283 + username: user.username, 284 + instance_name: config.lumina_synchronisation_iid, 285 + }) 286 + .unwrap(), 287 + side: String::new(), 288 + message: vec![899, 901], 289 + }, 290 + "test" => FEPageServeResponse { 291 + message: vec![], 292 + side: String::new(), 293 + main: { 294 + let mut s = format!( 295 + "<h1>Post fetched from DB (dynamically rendered using HandleBars)</h1>\n{}\n", 296 + &database::fetch::post(&config, ("pid", "1".to_string())) 297 + .unwrap() 298 + .unwrap() 299 + .to_formatted(&config) 300 + .to_html() 301 + ); 302 + s.push_str(include_str!("../frontend_assets/html/examplepost.html")); 303 + s 304 + }, 305 + }, 306 + "notifications-centre" => FEPageServeResponse { 307 + main: String::from("Notifications should show up here!"), 308 + side: String::from(""), 309 + message: vec![33], 310 + }, 311 + "editor" => FEPageServeResponse { 312 + main: String::from(crate::assets::STR_ASSETS_EDITOR_WINDOW_HTML), 313 + side: String::from(""), 314 + message: vec![34], 315 + }, 316 + 317 + _ => { 318 + return HttpResponse::build(StatusCode::OK) 319 + .insert_header(CacheControl(vec![CacheDirective::NoCache])) 320 + .content_type("text/json; charset=utf-8") 321 + .body( 322 + serde_json::to_string(&FEPageServeResponse { 323 + main: String::from( 324 + "This page does not exist according to the instance server.", 325 + ), 326 + side: String::new(), 327 + message: vec![2], 328 + }) 329 + .unwrap(), 330 + ) 331 + } 332 + }; 333 + HttpResponse::build(StatusCode::OK) 334 + .content_type("text/json; charset=utf-8") 335 + .insert_header(CacheControl(vec![CacheDirective::NoCache])) 336 + .body(serde_json::to_string(&o).unwrap()) 337 + } 338 + } 339 + } 340 + #[derive(Deserialize)] 341 + pub struct FEUsernameCheckRequest { 342 + u: String, 343 + } 344 + 345 + pub(crate) async fn check_username( 346 + server_vars_mutex: Data<Mutex<ServerVars>>, 347 + data: actix_web::web::Json<FEUsernameCheckRequest>, 348 + ) -> HttpResponse { 349 + let server_vars = ServerVars::grab(&server_vars_mutex).await; 350 + let config = server_vars.clone().config; 351 + let username = data.u.clone(); 352 + if database::users::char_check_username(username.clone()) { 353 + return HttpResponse::build(StatusCode::OK) 354 + .content_type("text/json; charset=utf-8") 355 + .insert_header(CacheControl(vec![CacheDirective::NoCache])) 356 + .body(r#"{"Ok": false, "Why": "InvalidChars"}"#.to_string()); 357 + } 358 + if username.len() < 4 { 359 + return HttpResponse::build(StatusCode::OK) 360 + .content_type("text/json; charset=utf-8") 361 + .insert_header(CacheControl(vec![CacheDirective::NoCache])) 362 + .body(r#"{"Ok": false, "Why": "TooShort"}"#.to_string()); 363 + } 364 + if database::fetch::user(&config.clone(), ("username", username.clone())) 365 + .unwrap_or(None) 366 + .is_some() 367 + { 368 + return HttpResponse::build(StatusCode::OK) 369 + .content_type("text/json; charset=utf-8") 370 + .insert_header(CacheControl(vec![CacheDirective::NoCache])) 371 + .body(r#"{"Ok": false, "Why": "userExists"}"#.to_string()); 372 + }; 373 + return HttpResponse::build(StatusCode::OK) 374 + .content_type("text/json; charset=utf-8") 375 + .insert_header(CacheControl(vec![CacheDirective::NoCache])) 376 + .body(r#"{"Ok": true}"#); 377 + } 378 + 379 + #[derive(Deserialize)] 380 + pub struct EditorContent { 381 + a: String, 382 + } 383 + #[derive(Serialize, Deserialize)] 384 + struct EditorResponse { 385 + #[serde(rename = "Ok")] 386 + ok: bool, 387 + #[serde(rename = "htmlContent")] 388 + html_content: String, 389 + } 390 + pub(crate) async fn render_editor_articlepost( 391 + server_vars_mutex: Data<Mutex<ServerVars>>, 392 + data: actix_web::web::Json<EditorContent>, 393 + ) -> HttpResponse { 394 + let server_vars = ServerVars::grab(&server_vars_mutex).await; 395 + let _config = server_vars.clone().config; 396 + let unprocessed_md = data.a.clone(); 397 + 398 + let processed_md = 399 + match markdown::to_html_with_options(unprocessed_md.as_str(), &markdown::Options::gfm()) { 400 + Ok(html) => html, 401 + Err(_) => { 402 + return HttpResponse::build(StatusCode::OK) 403 + .content_type("text/json; charset=utf-8") 404 + .insert_header(CacheControl(vec![CacheDirective::NoCache])) 405 + .body( 406 + serde_json::to_string(&EditorResponse { 407 + ok: false, 408 + html_content: String::from("Markdown processing failed."), 409 + }) 410 + .unwrap(), 411 + ); 412 + } 413 + }; 414 + let readied_html = processed_md 415 + .replace(r#"<img "#, r#"<img class="max-w-9/12" "#) 416 + .replace(r#"<a "#, r#"<a class="text-blue-400" "#) 417 + .replace(r#"<code>"#, r#"<code class="m-1 text-stone-500 bg-slate-200 dark:text-stone-200 dark:bg-slate-600">"#) 418 + .replace(r#"<blockquote>"#, r#"<blockquote class="p-0 [&>*]:pl-2 ml-3 mr-3 border-gray-300 border-s-4 bg-gray-50 dark:border-gray-500 dark:bg-gray-800">"#); 419 + return HttpResponse::build(StatusCode::OK) 420 + .insert_header(CacheControl(vec![CacheDirective::NoCache])) 421 + .content_type("text/json; charset=utf-8") 422 + .body( 423 + serde_json::to_string(&EditorResponse { 424 + ok: true, 425 + html_content: readied_html, 426 + }) 427 + .unwrap(), 428 + ); 429 + } 430 + 431 + mod subpagedata { 432 + use serde::Serialize; 433 + 434 + #[derive(Serialize)] 435 + #[serde(rename_all = "snake_case")] 436 + pub(super) struct HomePageData { 437 + pub(super) username: String, 438 + pub(super) instance_name: String, 439 + } 440 + } 441 + 442 + mod media;
+7
backend-rs/src/api_fe/media.rs
··· 1 + /* 2 + * Copyright (c) 2024, MLC 'Strawmelonjuice' Bloeiman 3 + * 4 + * Licensed under the BSD 3-Clause License. See the LICENSE file for more info. 5 + */ 6 + 7 + //! This module handles media-related API endpoints, such as uploading media.
+90
backend-rs/src/api_ii.rs
··· 1 + /* 2 + * Copyright (c) 2024, MLC 'Strawmelonjuice' Bloeiman 3 + * 4 + * Licensed under the BSD 3-Clause License. See the LICENSE file for more info. 5 + */ 6 + //! ## Inter-instance API's 7 + //! This module contains the inter-instance api's. It will mostly be used for pulling from other instances and syncing. 8 + 9 + use std::time::Duration; 10 + 11 + use crate::{database, LuminaConfig, ServerVars, SynclistItem}; 12 + use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; 13 + use tokio::time::sleep; 14 + 15 + pub(crate) async fn main(vars: ServerVars) { 16 + let progressbars = MultiProgress::new(); 17 + let config: LuminaConfig = vars.config.clone(); 18 + let mut round = 0; 19 + let mut sync_interval = config.lumina_synchronisation_interval; 20 + if sync_interval < 30 { 21 + sync_interval = 120 22 + }; 23 + // count before starting to sync 24 + let initial_waiting_time = sync_interval / 3; 25 + println!( 26 + "{}", 27 + vars.format_tell(format!( 28 + "Syncer: Syncing will start in {initial_waiting_time} seconds." 29 + )) 30 + .as_str() 31 + ); 32 + let waiting_counter = progressbars.add(ProgressBar::new(initial_waiting_time)); 33 + waiting_counter.enable_steady_tick(Duration::from_secs(1)); 34 + waiting_counter.set_style( 35 + ProgressStyle::with_template("{bar:60.white} {pos:>7}/{len:7} {msg}") 36 + .unwrap() 37 + .progress_chars("██░"), 38 + ); 39 + let mut remaining_waiting_time = initial_waiting_time; 40 + for _ in 0..initial_waiting_time { 41 + remaining_waiting_time -= 1; 42 + waiting_counter.inc(1); 43 + waiting_counter 44 + .set_message(vars.format_tell(format!("{} seconds left.", remaining_waiting_time))); 45 + sleep(Duration::from_secs(1)).await; 46 + } 47 + 48 + let number_of_instances: u64 = database::get_instance_sync_list(&config).unwrap_or(vec![]).len() as u64; 49 + if number_of_instances == 0 { 50 + waiting_counter.println(vars.format_tell("Syncer: No instances to sync from are listed. Syncer will close until further notice to preserve CPU threads.")); 51 + waiting_counter.finish_and_clear(); 52 + return; 53 + } 54 + let _s = if number_of_instances == 1 { "" } else { "s" }; 55 + waiting_counter.println(vars.format_tell(format!( 56 + "Syncer: Syncing from {number_of_instances} listed instance{_s} starting now." 57 + ))); 58 + waiting_counter.finish_and_clear(); 59 + loop { 60 + vars.tell(format!("Syncer: Syncing from {number_of_instances} listed instance{_s}, round {round}. Syncings are done every {sync_interval} seconds.")); 61 + round += 1; 62 + let sync_progress_through_instances = 63 + progressbars.add(ProgressBar::new(number_of_instances)); 64 + // bar.println(vars.format_tell("Syncing:")); 65 + sync_progress_through_instances.set_style( 66 + ProgressStyle::with_template("{bar:60.white} {pos:>7}/{len:7} {msg}") 67 + .unwrap() 68 + .progress_chars("██░"), 69 + ); 70 + for instance in database::get_instance_sync_list(&config).unwrap_or_default().into_iter() { 71 + sync_progress_through_instances.inc(1); 72 + sync_progress_through_instances 73 + .set_message(vars.format_tell(format!("Syncing: {}", instance.name))); 74 + // let sync_this_instance = MultiProgress::new().insert_after(&sync_progress_through_instances, ProgressBar::new(100)); 75 + // sync_this_instance.set_style(ProgressStyle::with_template("{bar:60.white} {pos:>7}/{len:7} {msg}").unwrap().progress_chars("██░")); 76 + sync_instance(instance.clone(), config.clone()); 77 + sleep(Duration::from_secs(3)).await; 78 + // sync_this_instance.finish(); 79 + } 80 + sync_progress_through_instances.finish_and_clear(); 81 + vars.tell(String::from("Syncer: Sync done.")); 82 + sleep(Duration::from_secs(sync_interval)).await; 83 + } 84 + } 85 + 86 + fn sync_instance(synclist_item: SynclistItem, config: LuminaConfig) { 87 + // Syncing code here. 88 + let _ = (synclist_item, config); 89 + // todo!("Syncing code here.") 90 + }
+71
backend-rs/src/assets.rs
··· 1 + /* 2 + * Copyright (c) 2024, MLC 'Strawmelonjuice' Bloeiman 3 + * 4 + * Licensed under the BSD 3-Clause License. See the LICENSE file for more info. 5 + */ 6 + 7 + pub const STR_ASSETS_INDEX_HTML: &str = include_str!("../frontend_assets/html/index.html"); 8 + pub const STR_ASSETS_LOGIN_HTML: &str = include_str!("../frontend_assets/html/login.html"); 9 + pub const STR_ASSETS_SIGNUP_HTML: &str = include_str!("../frontend_assets/html/signup.html"); 10 + pub const STR_ASSETS_HOME_HTML: &str = include_str!("../frontend_assets/html/home.html"); 11 + pub const STR_CLEAN_CUSTOMSTYLES_CSS: &str = 12 + include_str!("../frontend_assets/styles/initial_customstyles.css"); 13 + 14 + pub const STR_ASSETS_APPJS: &str = include_str!("../generated/js/app.js"); 15 + pub const STR_ASSETS_APPJS_MAP: &str = include_str!("../generated/js/app.js.map"); 16 + 17 + pub const STR_GENERATED_MAIN_MIN_CSS: &str = include_str!("../generated/css/main.min.css"); 18 + 19 + type Fontbytes = &'static [u8]; 20 + pub struct Fonts { 21 + pub josefin_sans: Fontbytes, 22 + pub fira_sans: Fontbytes, 23 + pub gantari: Fontbytes, 24 + pub syne: Fontbytes, 25 + } 26 + 27 + pub fn fonts() -> Fonts { 28 + Fonts { 29 + josefin_sans: include_bytes!( 30 + "../frontend_assets/fonts/Josefin_Sans/JosefinSans-VariableFont_wght.ttf" 31 + ), 32 + fira_sans: include_bytes!("../frontend_assets/fonts/Fira_Sans/FiraSans-Regular.ttf"), 33 + gantari: include_bytes!("../frontend_assets/fonts/Gantari/Gantari-VariableFont_wght.ttf"), 34 + syne: include_bytes!("../frontend_assets/fonts/Syne/Syne-VariableFont_wght.ttf"), 35 + } 36 + } 37 + 38 + pub const STR_ASSETS_LOGO_SVG: &str = include_str!("../frontend_assets/svg/luminalogo-1.svg"); 39 + 40 + pub const STR_ASSETS_BTN_NEW_SVG: &str = include_str!("../frontend_assets/svg/add.svg"); 41 + 42 + pub const STR_ASSETS_BTN_PUSH_SVG: &str = include_str!("../frontend_assets/svg/push.svg"); 43 + 44 + pub const STR_ASSETS_BTN_COMMENT_SVG: &str = include_str!("../frontend_assets/svg/comment.svg"); 45 + 46 + pub const STR_ASSETS_BTN_BOOST_SVG: &str = include_str!("../frontend_assets/svg/boost.svg"); 47 + 48 + pub const STR_ASSETS_GREEN_CHECK_SVG: &str = include_str!("../frontend_assets/svg/green_check.svg"); 49 + 50 + pub const STR_ASSETS_SPINNER_SVG: &str = include_str!("../frontend_assets/svg/spinner.svg"); 51 + 52 + pub const STR_ASSETS_RED_CROSS_SVG: &str = include_str!("../frontend_assets/svg/red_cross.svg"); 53 + pub const STR_ASSETS_ANON_SVG: &str = include_str!("../frontend_assets/svg/avatar1.svg"); 54 + 55 + pub fn vec_string_assets_anons_svg() -> Vec<String> { 56 + vec![ 57 + STR_ASSETS_ANON_SVG.to_string(), 58 + include_str!("../frontend_assets/svg/avatar2.svg").to_string(), 59 + include_str!("../frontend_assets/svg/avatar3.svg").to_string(), 60 + include_str!("../frontend_assets/svg/avatar4.svg").to_string(), 61 + include_str!("../frontend_assets/svg/avatar5.svg").to_string(), 62 + include_str!("../frontend_assets/svg/avatar6.svg").to_string(), 63 + ] 64 + } 65 + 66 + pub const BYTES_ASSETS_LOGO_PNG: &[u8] = include_bytes!("../frontend_assets/png/luminalogo-1.png"); 67 + 68 + pub const STR_ASSETS_EDITOR_WINDOW_HTML: &str = include_str!("../frontend_assets/html/writer.html"); 69 + 70 + pub const STR_ASSETS_POST_RENDERS_HANDLEBARS: &str = 71 + include_str!("../frontend_assets/handlebars/postrender.handlebars");
+237
backend-rs/src/config.rs
··· 1 + use crate::ERun; 2 + use colored::Colorize; 3 + use std::path::PathBuf; 4 + 5 + #[derive(Clone, Debug)] 6 + pub struct LuminaConfig { 7 + pub(crate) lumina_server_port: u16, 8 + pub(crate) lumina_server_addr: String, 9 + pub(crate) lumina_server_https: bool, 10 + pub(crate) lumina_synchronisation_iid: String, 11 + pub(crate) lumina_synchronisation_interval: u64, 12 + pub(crate) db_custom_salt: String, 13 + pub(crate) db_connection_info: LuminaDBConnectionInfo, 14 + pub(crate) logging: Option<LuminaLogConfig>, 15 + /// Run time information 16 + pub(crate) erun: ERun, 17 + } 18 + 19 + #[derive(Clone, Debug)] 20 + pub enum LuminaDBConnectionInfo { 21 + LuminaDBConnectionInfoPOSTGRES(postgres::Config), 22 + LuminaDBConnectionInfoSQLite(PathBuf), 23 + } 24 + 25 + #[derive(Default, Debug, Clone, PartialEq)] 26 + pub struct LuminaLogConfig { 27 + pub file_loglevel: Option<u8>, 28 + pub term_loglevel: Option<u8>, 29 + pub logfile: Option<String>, 30 + } 31 + #[derive(Debug)] 32 + enum DBType { 33 + POSTGRES, 34 + SQLITE, 35 + } 36 + impl LuminaConfig { 37 + /// Loads the configuration from environment variables after reading an env file if it exists. 38 + pub fn new(erun: ERun) -> Self { 39 + if erun.cd.join(".env").exists() { 40 + if dotenv::from_path(erun.cd.join(".env")).is_ok() { 41 + info!( 42 + "Loaded environment variables from '{}' file.", 43 + erun.cd.join(".env").display() 44 + ); 45 + }; 46 + } else { 47 + info!( 48 + "No '.env' file found in the directory '{}'. Using only environment variables.", 49 + erun.cd.display() 50 + ); 51 + } 52 + 53 + let db_type: DBType = { 54 + match std::env::var("_LUMINA_DB_TYPE_") 55 + .unwrap_or(String::from("SQLITE")) 56 + .to_uppercase() 57 + .as_str() 58 + { 59 + "POSTGRES" => DBType::POSTGRES, 60 + _ => { 61 + warn!("{}", "Using SQLITE database, this is not recommended for production as it is not scalable.".yellow()); 62 + info!("{}","To use a different database, set the '_LUMINA_DB_TYPE_' environment variable to 'POSTGRES'.".purple()); 63 + DBType::SQLITE 64 + } 65 + } 66 + }; 67 + let db_connection_info = match db_type { 68 + DBType::SQLITE => { 69 + let keyname = "_LUMINA_SQLITE_FILE_"; 70 + let def_val = String::from("instance.db"); 71 + match std::env::var(keyname) { 72 + Ok(val) => { 73 + LuminaDBConnectionInfo::LuminaDBConnectionInfoSQLite(erun.cd.join(val)) 74 + } 75 + Err(_) => { 76 + warn!("The key '{}' was not found in the environment variables. Using default: {}", keyname, def_val); 77 + LuminaDBConnectionInfo::LuminaDBConnectionInfoSQLite(erun.cd.join(def_val)) 78 + } 79 + } 80 + } 81 + DBType::POSTGRES => { 82 + let mut pg_config = postgres::Config::new(); 83 + { 84 + match std::env::var("_LUMINA_POSTGRES_USERNAME_") { 85 + Ok(val) => { 86 + pg_config.user(&val); 87 + } 88 + Err(_) => { 89 + error!("No Postgres database user provided under environment variable '_LUMINA_POSTGRES_USER_'. Exiting."); 90 + std::process::exit(1); 91 + } 92 + } 93 + } 94 + match std::env::var("_LUMINA_POSTGRES_HOST_") { 95 + Ok(val) => { 96 + pg_config.host(&val); 97 + } 98 + Err(_) => { 99 + warn!("No Postgres database host provided under environment variable '_LUMINA_POSTGRES_HOST_'. Using default value 'localhost'."); 100 + pg_config.host("localhost"); 101 + } 102 + }; 103 + match std::env::var("_LUMINA_POSTGRES_PASSWORD_") { 104 + Ok(val) => { 105 + pg_config.password(&val); 106 + } 107 + Err(_) => { 108 + info!("No Postgres database password provided under environment variable '_LUMINA_POSTGRES_PASSWORD_'. Using passwordless connection."); 109 + } 110 + }; 111 + match std::env::var("_LUMINA_POSTGRES_DATABASE_") { 112 + Ok(val) => { 113 + pg_config.dbname(&val); 114 + } 115 + Err(_) => { 116 + error!("No Postgres database name provided under environment variable '_LUMINA_POSTGRES_DATABASE_'. Exiting."); 117 + std::process::exit(1); 118 + } 119 + }; 120 + { 121 + match std::env::var("_LUMINA_POSTGRES_PORT_") { 122 + Ok(val) => match val.parse::<u16>() { 123 + Ok(v) => { 124 + pg_config.port(v); 125 + } 126 + Err(_) => { 127 + error!("The value '{}' for the key '_LUMINA_POSTGRES_PORT_' is not a valid port number. Exiting.", val); 128 + std::process::exit(1); 129 + } 130 + }, 131 + Err(_) => { 132 + warn!("No Postgres database port provided under environment variable '_LUMINA_POSTGRES_PORT_'. Using default value '5432'."); 133 + pg_config.port(5432); 134 + } 135 + } 136 + } 137 + 138 + LuminaDBConnectionInfo::LuminaDBConnectionInfoPOSTGRES(pg_config) 139 + } 140 + }; 141 + 142 + LuminaConfig { 143 + lumina_server_port: { 144 + let keyname = "_LUMINA_SERVER_PORT_"; 145 + let def_val = String::from("8085"); 146 + match std::env::var(keyname) { 147 + Ok(val) => match val.parse::<u16>() { 148 + Ok(v) => v, 149 + Err(_) => { 150 + error!("The value '{}' for the key '{}' is not a valid port number. Exiting.", val, keyname); 151 + std::process::exit(1); 152 + } 153 + }, 154 + Err(_) => { 155 + warn!("The key '{}' was not found in the environment variables. Using default value.", keyname); 156 + def_val.parse::<u16>().unwrap() 157 + } 158 + } 159 + }, 160 + lumina_server_addr: { 161 + let keyname = "_LUMINA_SERVER_ADDR_"; 162 + let def_val = String::from("localhost"); 163 + match std::env::var(keyname) { 164 + Ok(val) => val, 165 + Err(_) => { 166 + warn!("The key '{}' was not found in the environment variables. Using default value.", keyname); 167 + def_val 168 + } 169 + } 170 + }, 171 + lumina_server_https: { 172 + let keyname = "_LUMINA_SERVER_HTTPS_"; 173 + let def_val = String::from("true"); 174 + match std::env::var(keyname) { 175 + Ok(val) => { 176 + match val.parse::<bool>() { 177 + Ok(v) => v, 178 + Err(_) => { 179 + error!("The value '{}' for the key '{}' is not a valid boolean. Exiting.", val, keyname); 180 + std::process::exit(1); 181 + } 182 + } 183 + } 184 + Err(_) => { 185 + warn!("The key '{}' was not found in the environment variables. Using default value.", keyname); 186 + def_val.parse::<bool>().unwrap() 187 + } 188 + } 189 + }, 190 + lumina_synchronisation_iid: { 191 + let keyname = "_LUMINA_SYNCHRONISATION_IID_"; 192 + let def_val = String::from("localhost"); 193 + match std::env::var(keyname) { 194 + Ok(val) => val, 195 + Err(_) => { 196 + warn!("The key '{}' was not found in the environment variables. Using default value.", keyname); 197 + def_val 198 + } 199 + } 200 + }, 201 + lumina_synchronisation_interval: { 202 + let keyname = "_LUMINA_SYNCHRONISATION_INTERVAL_"; 203 + let def_val = String::from("30"); 204 + match std::env::var(keyname) { 205 + Ok(val) => { 206 + match val.parse::<u64>() { 207 + Ok(v) => v, 208 + Err(_) => { 209 + error!("The value '{}' for the key '{}' is not a valid number. Exiting.", val, keyname); 210 + std::process::exit(1); 211 + } 212 + } 213 + } 214 + Err(_) => { 215 + warn!("The key '{}' was not found in the environment variables. Using default value.", keyname); 216 + def_val.parse::<u64>().unwrap() 217 + } 218 + } 219 + }, 220 + db_custom_salt: { 221 + let keyname = "_LUMINA_DB_SALT_"; 222 + let def_val = String::from("sally_sal"); 223 + match std::env::var(keyname) { 224 + Ok(val) => val, 225 + Err(_) => { 226 + warn!("The key '{}' was not found in the environment variables. Using default value.", keyname); 227 + def_val 228 + } 229 + } 230 + }, 231 + db_connection_info, 232 + // Uses the default value of Logging::default() fer now. 233 + logging: None, 234 + erun, 235 + } 236 + } 237 + }
+676
backend-rs/src/database.rs
··· 1 + /* 2 + * Copyright (c) 2024, MLC 'Strawmelonjuice' Bloeiman 3 + * 4 + * Licensed under the BSD 3-Clause License. See the LICENSE file for more info. 5 + */ 6 + #![allow(dead_code)] 7 + 8 + use crate::config::LuminaDBConnectionInfo; 9 + use crate::post::PostInfo; 10 + use crate::{LuminaConfig, SynclistItem}; 11 + use colored::Colorize; 12 + use rusqlite::{params, Connection}; 13 + use serde::{Deserialize, Serialize}; 14 + use std::any::type_name; 15 + use std::io::Error; 16 + use std::process; 17 + use LuminaDBConnectionInfo::{LuminaDBConnectionInfoPOSTGRES, LuminaDBConnectionInfoSQLite}; 18 + use users::User; 19 + 20 + /// Basic exchangable user information. 21 + #[derive(Debug, Serialize, Deserialize, Clone)] 22 + pub struct IIExchangedUserInfo { 23 + /// User ID 24 + pub(crate) id: i64, 25 + /// Known username 26 + pub(crate) username: String, 27 + /// Instance ID 28 + pub(crate) instance: String, 29 + } 30 + 31 + impl LuminaConfig { 32 + /// Create a database connection to either SQLite or POSTGRES. 33 + pub(crate) fn db_connect(&self) -> LuminaDBConnection { 34 + match self.db_connection_info.clone() { 35 + LuminaDBConnectionInfoSQLite(file) => match Connection::open(file.clone()) { 36 + Ok(d) => LuminaDBConnection::SQLite(d), 37 + Err(_e) => { 38 + error!( 39 + "Could not create a database connection to <{}>", 40 + file.display().to_string().yellow() 41 + ); 42 + process::exit(1); 43 + } 44 + }, 45 + LuminaDBConnectionInfoPOSTGRES(pg_config) => { 46 + let f = pg_config.connect(postgres::tls::NoTls).unwrap(); 47 + LuminaDBConnection::POSTGRES(f) 48 + } 49 + } 50 + } 51 + } 52 + 53 + pub enum LuminaDBConnection { 54 + SQLite(Connection), 55 + POSTGRES(postgres::Client), 56 + } 57 + 58 + impl LuminaDBConnection { 59 + pub(crate) fn initial_dbconf(&mut self) { 60 + fn emergencyabort() { 61 + error!("Could not configure the database correctly!"); 62 + process::exit(1); 63 + } 64 + let qs = [ 65 + "CREATE TABLE IF NOT EXISTS external_posts( 66 + host_id INTEGER PRIMARY KEY, 67 + source_id INTEGER NOT NULL, 68 + source_instance TEXT NOT NULL 69 + );", 70 + "CREATE TABLE IF NOT EXISTS interinstance_relations( 71 + instance_id TEXT PRIMARY KEY, 72 + synclevel TEXT NOT NULL, 73 + last_contact INTEGER NOT NULL 74 + );", 75 + "CREATE TABLE IF NOT EXISTS local_posts( 76 + host_id INTEGER PRIMARY KEY, 77 + user_id INTEGER NOT NULL, 78 + privacy INTEGER NOT NULL 79 + );", 80 + "CREATE TABLE IF NOT EXISTS posts_pool( 81 + postid INTEGER PRIMARY KEY, 82 + kind TEXT NOT NULL, 83 + content TEXT NOT NULL, 84 + from_local INTEGER NOT NULL 85 + );", 86 + "CREATE TABLE IF NOT EXISTS users( 87 + id INTEGER PRIMARY KEY, 88 + username TEXT NOT NULL, 89 + password TEXT NOT NULL, 90 + email TEXT NOT NULL 91 + );", 92 + ]; 93 + 94 + for query in qs { 95 + match self.execute0(query) { 96 + Ok(_) => {} 97 + Err(_e) => emergencyabort(), 98 + } 99 + } 100 + } 101 + pub(crate) fn execute0(&mut self, query: &str) -> Result<(), ()> { 102 + match self { 103 + LuminaDBConnection::SQLite(conn) => { 104 + conn.execute(query, ()).map(|_r| ()).map_err(|_e| ()) 105 + } 106 + LuminaDBConnection::POSTGRES(client) => { 107 + client.execute(query, &[]).map(|_r| ()).map_err(|_e| ()) 108 + } 109 + } 110 + } 111 + pub(crate) fn execute1(&mut self, query: &str, param: impl AsRef<str>) -> Result<(), ()> { 112 + match self { 113 + LuminaDBConnection::SQLite(conn) => conn 114 + .execute(query, &[&param.as_ref()]) 115 + .map(|_r| ()) 116 + .map_err(|_e| ()), 117 + LuminaDBConnection::POSTGRES(client) => client 118 + .execute(query, &[&param.as_ref()]) 119 + .map(|_r| ()) 120 + .map_err(|_e| ()), 121 + } 122 + } 123 + pub(crate) fn execute2( 124 + &mut self, 125 + query: &str, 126 + params: (impl AsRef<str>, impl AsRef<str>), 127 + ) -> Result<(), ()> { 128 + match self { 129 + LuminaDBConnection::SQLite(conn) => conn 130 + .execute(query, &[&params.0.as_ref(), &params.1.as_ref()]) 131 + .map(|_r| ()) 132 + .map_err(|_e| ()), 133 + LuminaDBConnection::POSTGRES(client) => client 134 + .execute(query, &[&params.0.as_ref(), &params.1.as_ref()]) 135 + .map(|_r| ()) 136 + .map_err(|_e| ()), 137 + } 138 + } 139 + pub(crate) fn execute3( 140 + &mut self, 141 + query: &str, 142 + params: (impl AsRef<str>, impl AsRef<str>, impl AsRef<str>), 143 + ) -> Result<(), ()> { 144 + match self { 145 + LuminaDBConnection::SQLite(conn) => conn 146 + .execute( 147 + query, 148 + &[ 149 + &(params.0.as_ref()), 150 + &(params.1.as_ref()), 151 + &(params.2.as_ref()), 152 + ], 153 + ) 154 + .map(|_r| ()) 155 + .map_err(|_e| ()), 156 + LuminaDBConnection::POSTGRES(client) => client 157 + .execute( 158 + query, 159 + &[ 160 + &(params.0.as_ref()), 161 + &(params.1.as_ref()), 162 + &(params.2.as_ref()), 163 + ], 164 + ) 165 + .map(|_r| ()) 166 + .map_err(|_e| ()), 167 + } 168 + } 169 + pub(crate) fn execute4( 170 + &mut self, 171 + query: &str, 172 + params: ( 173 + impl AsRef<str>, 174 + impl AsRef<str>, 175 + impl AsRef<str>, 176 + impl AsRef<str>, 177 + ), 178 + ) -> Result<(), ()> { 179 + match self { 180 + LuminaDBConnection::SQLite(conn) => conn 181 + .execute( 182 + query, 183 + &[ 184 + &(params.0.as_ref()), 185 + &(params.1.as_ref()), 186 + &(params.2.as_ref()), 187 + &(params.3.as_ref()), 188 + ], 189 + ) 190 + .map(|_r| ()) 191 + .map_err(|_e| ()), 192 + LuminaDBConnection::POSTGRES(client) => client 193 + .execute( 194 + query, 195 + &[ 196 + &(params.0.as_ref()), 197 + &(params.1.as_ref()), 198 + &(params.2.as_ref()), 199 + &(params.3.as_ref()), 200 + ], 201 + ) 202 + .map(|_r| ()) 203 + .map_err(|_e| ()), 204 + } 205 + } 206 + pub(crate) fn execute5( 207 + &mut self, 208 + query: &str, 209 + params: ( 210 + impl AsRef<str>, 211 + impl AsRef<str>, 212 + impl AsRef<str>, 213 + impl AsRef<str>, 214 + impl AsRef<str>, 215 + ), 216 + ) -> Result<(), ()> { 217 + match self { 218 + LuminaDBConnection::SQLite(conn) => conn 219 + .execute( 220 + query, 221 + &[ 222 + &(params.0.as_ref()), 223 + &(params.1.as_ref()), 224 + &(params.2.as_ref()), 225 + &(params.3.as_ref()), 226 + &(params.4.as_ref()), 227 + ], 228 + ) 229 + .map(|_r| ()) 230 + .map_err(|_e| ()), 231 + LuminaDBConnection::POSTGRES(client) => client 232 + .execute( 233 + query, 234 + &[ 235 + &(params.0.as_ref()), 236 + &(params.1.as_ref()), 237 + &(params.2.as_ref()), 238 + &(params.3.as_ref()), 239 + &(params.4.as_ref()), 240 + ], 241 + ) 242 + .map(|_r| ()) 243 + .map_err(|_e| ()), 244 + } 245 + } 246 + pub fn execute6( 247 + &mut self, 248 + query: &str, 249 + params: ( 250 + impl AsRef<str>, 251 + impl AsRef<str>, 252 + impl AsRef<str>, 253 + impl AsRef<str>, 254 + impl AsRef<str>, 255 + impl AsRef<str>, 256 + ), 257 + ) -> Result<(), ()> { 258 + match self { 259 + LuminaDBConnection::SQLite(conn) => conn 260 + .execute( 261 + query, 262 + &[ 263 + &(params.0.as_ref()), 264 + &(params.1.as_ref()), 265 + &(params.2.as_ref()), 266 + &(params.3.as_ref()), 267 + &(params.4.as_ref()), 268 + &(params.5.as_ref()), 269 + ], 270 + ) 271 + .map(|_r| ()) 272 + .map_err(|_e| ()), 273 + LuminaDBConnection::POSTGRES(client) => client 274 + .execute( 275 + query, 276 + &[ 277 + &(params.0.as_ref()), 278 + &(params.1.as_ref()), 279 + &(params.2.as_ref()), 280 + &(params.3.as_ref()), 281 + &(params.4.as_ref()), 282 + &(params.5.as_ref()), 283 + ], 284 + ) 285 + .map(|_r| ()) 286 + .map_err(|_e| ()), 287 + } 288 + } 289 + 290 + pub fn execute7( 291 + &mut self, 292 + query: &str, 293 + params: ( 294 + impl AsRef<str>, 295 + impl AsRef<str>, 296 + impl AsRef<str>, 297 + impl AsRef<str>, 298 + impl AsRef<str>, 299 + impl AsRef<str>, 300 + impl AsRef<str>, 301 + ), 302 + ) -> Result<(), ()> { 303 + match self { 304 + LuminaDBConnection::SQLite(conn) => conn 305 + .execute( 306 + query, 307 + &[ 308 + &(params.0.as_ref()), 309 + &(params.1.as_ref()), 310 + &(params.2.as_ref()), 311 + &(params.3.as_ref()), 312 + &(params.4.as_ref()), 313 + &(params.5.as_ref()), 314 + &(params.6.as_ref()), 315 + ], 316 + ) 317 + .map(|_r| ()) 318 + .map_err(|_e| ()), 319 + LuminaDBConnection::POSTGRES(client) => client 320 + .execute( 321 + query, 322 + &[ 323 + &(params.0.as_ref()), 324 + &(params.1.as_ref()), 325 + &(params.2.as_ref()), 326 + &(params.3.as_ref()), 327 + &(params.4.as_ref()), 328 + &(params.5.as_ref()), 329 + &(params.6.as_ref()), 330 + ], 331 + ) 332 + .map(|_r| ()) 333 + .map_err(|_e| ()), 334 + } 335 + } 336 + pub fn execute8( 337 + &mut self, 338 + query: &str, 339 + params: ( 340 + impl AsRef<str>, 341 + impl AsRef<str>, 342 + impl AsRef<str>, 343 + impl AsRef<str>, 344 + impl AsRef<str>, 345 + impl AsRef<str>, 346 + impl AsRef<str>, 347 + impl AsRef<str>, 348 + ), 349 + ) -> Result<(), ()> { 350 + match self { 351 + LuminaDBConnection::SQLite(conn) => conn 352 + .execute( 353 + query, 354 + &[ 355 + &(params.0.as_ref()), 356 + &(params.1.as_ref()), 357 + &(params.2.as_ref()), 358 + &(params.3.as_ref()), 359 + &(params.4.as_ref()), 360 + &(params.5.as_ref()), 361 + &(params.6.as_ref()), 362 + &(params.7.as_ref()), 363 + ], 364 + ) 365 + .map(|_r| ()) 366 + .map_err(|_e| ()), 367 + LuminaDBConnection::POSTGRES(client) => client 368 + .execute( 369 + query, 370 + &[ 371 + &(params.0.as_ref()), 372 + &(params.1.as_ref()), 373 + &(params.2.as_ref()), 374 + &(params.3.as_ref()), 375 + &(params.4.as_ref()), 376 + &(params.5.as_ref()), 377 + &(params.6.as_ref()), 378 + &(params.7.as_ref()), 379 + ], 380 + ) 381 + .map(|_r| ()) 382 + .map_err(|_e| ()), 383 + } 384 + } 385 + pub fn execute9( 386 + &mut self, 387 + query: &str, 388 + params: ( 389 + impl AsRef<str>, 390 + impl AsRef<str>, 391 + impl AsRef<str>, 392 + impl AsRef<str>, 393 + impl AsRef<str>, 394 + impl AsRef<str>, 395 + impl AsRef<str>, 396 + impl AsRef<str>, 397 + impl AsRef<str>, 398 + ), 399 + ) -> Result<(), ()> { 400 + match self { 401 + LuminaDBConnection::SQLite(conn) => conn 402 + .execute( 403 + query, 404 + &[ 405 + &(params.0.as_ref()), 406 + &(params.1.as_ref()), 407 + &(params.2.as_ref()), 408 + &(params.3.as_ref()), 409 + &(params.4.as_ref()), 410 + &(params.5.as_ref()), 411 + &(params.6.as_ref()), 412 + &(params.7.as_ref()), 413 + &(params.8.as_ref()), 414 + ], 415 + ) 416 + .map(|_r| ()) 417 + .map_err(|_e| ()), 418 + LuminaDBConnection::POSTGRES(client) => client 419 + .execute( 420 + query, 421 + &[ 422 + &(params.0.as_ref()), 423 + &(params.1.as_ref()), 424 + &(params.2.as_ref()), 425 + &(params.3.as_ref()), 426 + &(params.4.as_ref()), 427 + &(params.5.as_ref()), 428 + &(params.6.as_ref()), 429 + &(params.7.as_ref()), 430 + &(params.8.as_ref()), 431 + ], 432 + ) 433 + .map(|_r| ()) 434 + .map_err(|_e| ()), 435 + } 436 + } 437 + pub fn execute10( 438 + &mut self, 439 + query: &str, 440 + params: ( 441 + impl AsRef<str>, 442 + impl AsRef<str>, 443 + impl AsRef<str>, 444 + impl AsRef<str>, 445 + impl AsRef<str>, 446 + impl AsRef<str>, 447 + impl AsRef<str>, 448 + impl AsRef<str>, 449 + impl AsRef<str>, 450 + impl AsRef<str>, 451 + ), 452 + ) -> Result<(), ()> { 453 + match self { 454 + LuminaDBConnection::SQLite(conn) => conn 455 + .execute( 456 + query, 457 + &[ 458 + &(params.0.as_ref()), 459 + &(params.1.as_ref()), 460 + &(params.2.as_ref()), 461 + &(params.3.as_ref()), 462 + &(params.4.as_ref()), 463 + &(params.5.as_ref()), 464 + &(params.6.as_ref()), 465 + &(params.7.as_ref()), 466 + &(params.8.as_ref()), 467 + &(params.9.as_ref()), 468 + ], 469 + ) 470 + .map(|_r| ()) 471 + .map_err(|_e| ()), 472 + LuminaDBConnection::POSTGRES(client) => client 473 + .execute( 474 + query, 475 + &[ 476 + &(params.0.as_ref()), 477 + &(params.1.as_ref()), 478 + &(params.2.as_ref()), 479 + &(params.3.as_ref()), 480 + &(params.4.as_ref()), 481 + &(params.5.as_ref()), 482 + &(params.6.as_ref()), 483 + &(params.7.as_ref()), 484 + &(params.8.as_ref()), 485 + &(params.9.as_ref()), 486 + ], 487 + ) 488 + .map(|_r| ()) 489 + .map_err(|_e| ()), 490 + } 491 + } 492 + } 493 + 494 + pub trait DatabaseItem {} 495 + impl DatabaseItem for PostInfo {} 496 + 497 + pub enum UniversalFetchAnswer { 498 + Post(PostInfo), 499 + User(User), 500 + None, 501 + Err(Error), 502 + } 503 + impl UniversalFetchAnswer { 504 + /// Unwraps a post from a fetch answer. 505 + /// # Panics 506 + /// Will panic if the fetch answer is not a post. 507 + /// # Returns 508 + /// * `PostInfo` - The post metadata. 509 + pub fn unwrap_post(self) -> PostInfo { 510 + match self { 511 + UniversalFetchAnswer::Post(s) => s, 512 + UniversalFetchAnswer::None => { 513 + panic!("Attempted to unwrap a post from a None value!"); 514 + } 515 + UniversalFetchAnswer::Err(_) => { 516 + panic!("Attempted to unwrap a post from an error!"); 517 + } 518 + _ => { 519 + panic!("Attempted to unwrap a post from a different type of fetch!"); 520 + } 521 + } 522 + } 523 + /// Unwraps a user from a fetch answer. 524 + /// # Panics 525 + /// Panics if the fetch answer is not a user. 526 + /// # Returns 527 + /// * `BasicUserInfo` - The user metadata. 528 + pub fn unwrap_user(self) -> User { 529 + match self { 530 + UniversalFetchAnswer::User(s) => s, 531 + UniversalFetchAnswer::None => { 532 + panic!("Attempted to unwrap a user from a None value!"); 533 + } 534 + UniversalFetchAnswer::Err(_) => { 535 + panic!("Attempted to unwrap a user from an error!"); 536 + } 537 + _ => { 538 + panic!("Attempted to unwrap a user from a different type of fetch!"); 539 + } 540 + } 541 + } 542 + } 543 + 544 + /// # `storage::unifetch<>()` 545 + /// Fetches well-known data types from the database. 546 + /// # Arguments 547 + /// * `config` - A reference to the LuminaConfig struct. 548 + /// * `discriminator` - A tuple containing the discriminator and the value to search for. 549 + /// # Returns 550 + /// * `UniversalFetchAnswer` - The fetched data. Wrapped in an enum that can be unwrapped to the specific type. 551 + /// # Panics 552 + /// Panics if the database type is not supported. 553 + /// # Note 554 + /// **Discouraged: Use the specific fetch functions instead.** 555 + /// # Example 556 + /// ```rust 557 + /// let config = LuminaConfig::default(); 558 + /// let discriminator = ("username", "admin"); 559 + /// let user = storage::unifetch::<BasicUserInfo>(&config, discriminator).unwrap_user(); 560 + /// ``` 561 + pub fn unifetch<T: DatabaseItem>( 562 + config: &LuminaConfig, 563 + discriminator: (impl AsRef<str>, impl AsRef<str>), 564 + ) -> UniversalFetchAnswer { 565 + if type_name::<T>() == type_name::<PostInfo>() { 566 + let sres = fetch::post(config, discriminator); 567 + let res = match sres { 568 + Ok(s) => s, 569 + Err(e) => return UniversalFetchAnswer::Err(e), 570 + }; 571 + match res { 572 + Some(s) => UniversalFetchAnswer::Post(s), 573 + None => UniversalFetchAnswer::None, 574 + } 575 + } else if type_name::<T>() == type_name::<User>() { 576 + let sres = fetch::user(config, discriminator); 577 + let res = match sres { 578 + Ok(s) => s, 579 + Err(e) => return UniversalFetchAnswer::Err(e), 580 + }; 581 + match res { 582 + Some(s) => UniversalFetchAnswer::User(s), 583 + None => UniversalFetchAnswer::None, 584 + } 585 + } else { 586 + error!("Unknown or unsupported database type! Only SQLITE is supported as of now."); 587 + process::exit(1); 588 + } 589 + } 590 + 591 + pub fn fetch_posts_in_range( 592 + config: &LuminaConfig, 593 + start: i64, 594 + end: i64, 595 + ) -> Result<Vec<PostInfo>, Error> { 596 + match config.db_connect() { 597 + LuminaDBConnection::SQLite(conn) => { 598 + let mut stmt = conn 599 + .prepare("SELECT * FROM PostsStore WHERE timestamp BETWEEN ?1 AND ?2") 600 + .unwrap(); 601 + 602 + let post_rows = stmt 603 + .query_map(params![start, end], |row| Ok(row_to_post_info(row))) 604 + .unwrap(); 605 + 606 + let mut posts = Vec::new(); 607 + for post_row in post_rows { 608 + posts.push(post_row.unwrap()); 609 + } 610 + 611 + Ok(posts) 612 + } 613 + LuminaDBConnection::POSTGRES(_) => { 614 + todo!("Postgres not implemented yet."); 615 + } 616 + } 617 + } 618 + 619 + fn row_to_post_info(row: &rusqlite::Row) -> PostInfo { 620 + PostInfo { 621 + lpid: row.get(0).unwrap(), 622 + pid: row.get(1).unwrap(), 623 + instance: row.get(2).unwrap(), 624 + author_id: row.get(3).unwrap(), 625 + timestamp: row.get(4).unwrap(), 626 + content_type: row.get(5).unwrap(), 627 + content: row.get(6).unwrap(), 628 + tags: row.get(7).unwrap(), 629 + } 630 + } 631 + 632 + pub(crate) mod fetch; 633 + pub(crate) mod users; 634 + 635 + pub fn get_instance_sync_list(config: &LuminaConfig) -> Result<Vec<SynclistItem>, Error> { 636 + match config.db_connect() { 637 + LuminaDBConnection::SQLite(conn) => { 638 + let mut stmt = conn 639 + .prepare("SELECT * FROM interinstance_relations") 640 + .unwrap(); 641 + 642 + let sync_list_rows = stmt 643 + .query_map((), |row| { 644 + Ok(SynclistItem { 645 + name: row.get(0).unwrap(), 646 + level: row.get(1).unwrap(), 647 + last_contact: row.get(2).unwrap(), 648 + }) 649 + }) 650 + .unwrap(); 651 + 652 + let mut sync_list = Vec::new(); 653 + for sync_list_row in sync_list_rows { 654 + sync_list.push(sync_list_row.unwrap()); 655 + } 656 + 657 + Ok(sync_list) 658 + } 659 + LuminaDBConnection::POSTGRES(mut client) => { 660 + let rows = client 661 + .query("SELECT * FROM interinstance_relations", &[]) 662 + .unwrap(); 663 + 664 + let mut sync_list = Vec::new(); 665 + for row in rows { 666 + sync_list.push(SynclistItem { 667 + name: row.get(0), 668 + level: row.get(1), 669 + last_contact: row.get(2), 670 + }); 671 + } 672 + 673 + Ok(sync_list) 674 + } 675 + } 676 + }
+112
backend-rs/src/database/fetch.rs
··· 1 + /* 2 + * Copyright (c) 2024, MLC 'Strawmelonjuice' Bloeiman 3 + * 4 + * Licensed under the BSD 3-Clause License. See the LICENSE file for more info. 5 + */ 6 + 7 + use crate::database::{LuminaDBConnection}; 8 + use crate::post::PostInfo; 9 + use crate::{database, LuminaConfig}; 10 + use std::io::{Error, ErrorKind}; 11 + use crate::database::users::User; 12 + 13 + /// Fetches user from database 14 + pub fn user( 15 + config: &LuminaConfig, 16 + discriminator: (impl AsRef<str>, impl AsRef<str>), 17 + ) -> Result<Option<User>, Error> { 18 + match config.db_connect() { 19 + LuminaDBConnection::SQLite(conn) => { 20 + let mut stmt = conn 21 + .prepare( 22 + format!( 23 + r#"select * from Users where {0} = '{1}'"#, 24 + discriminator.0.as_ref(), 25 + discriminator.1.as_ref() 26 + ) 27 + .trim(), 28 + ) 29 + .unwrap(); 30 + debug!("{:?}", stmt); 31 + let mut res = stmt 32 + .query_map((), |row| { 33 + Ok({ 34 + serde_json::to_string(&User { 35 + id: row.get(0)?, 36 + username: row.get(1)?, 37 + password: row.get(2)?, 38 + email: row.get(3)?, 39 + }) 40 + .unwrap() 41 + }) 42 + }) 43 + .unwrap(); 44 + // println!("{:?}", res.nth(0)); 45 + match res.next() { 46 + None => Ok(None), 47 + Some(r) => match r { 48 + Ok(s) => { 49 + let res: User = serde_json::from_str(&s).unwrap(); 50 + Ok(Some(res)) 51 + } 52 + Err(f) => { 53 + eprintln!("{:?}", f); 54 + Err(Error::new(ErrorKind::Other, "Unparseable data.")) 55 + } 56 + }, 57 + } 58 + } 59 + LuminaDBConnection::POSTGRES(_) => { 60 + todo!("Postgres not implemented yet."); 61 + } 62 + } 63 + } 64 + 65 + /// Fetches post from database 66 + pub fn post( 67 + config: &LuminaConfig, 68 + discriminator: (impl AsRef<str>, impl AsRef<str>), 69 + ) -> Result<Option<PostInfo>, Error> { 70 + match config.db_connect() { 71 + LuminaDBConnection::SQLite(conn) => { 72 + 73 + 74 + let mut stmt = conn 75 + .prepare( 76 + format!( 77 + r#"select * from PostsStore where {0} = '{1}'"#, 78 + discriminator.0.as_ref(), 79 + discriminator.1.as_ref() 80 + ) 81 + .trim(), 82 + ) 83 + .unwrap(); 84 + debug!("{:?}", stmt); 85 + let mut res = stmt 86 + .query_map((), |row| { 87 + Ok({ 88 + let s = database::row_to_post_info(row); 89 + serde_json::to_string(&s).unwrap() 90 + }) 91 + }) 92 + .unwrap(); 93 + // println!("{:?}", res.nth(0)); 94 + match res.next() { 95 + None => Ok(None), 96 + Some(r) => match r { 97 + Ok(s) => { 98 + let res: PostInfo = serde_json::from_str(&s)?; 99 + Ok(Some(res)) 100 + } 101 + Err(f) => { 102 + eprintln!("{:?}", f); 103 + Err(Error::new(ErrorKind::Other, "Unparseable data.")) 104 + } 105 + }, 106 + } 107 + } 108 + LuminaDBConnection::POSTGRES(_) => { 109 + todo!("Postgres not implemented yet."); 110 + } 111 + } 112 + }
+253
backend-rs/src/database/users.rs
··· 1 + /* 2 + * Copyright (c) 2024, MLC 'Strawmelonjuice' Bloeiman 3 + * 4 + * Licensed under the BSD 3-Clause License. See the LICENSE file for more info. 5 + */ 6 + 7 + use std::io::{Error, ErrorKind}; 8 + 9 + use magic_crypt::{new_magic_crypt, MagicCryptTrait}; 10 + use serde::{Deserialize, Serialize}; 11 + use crate::{database, LuminaConfig}; 12 + 13 + use super::{DatabaseItem, IIExchangedUserInfo}; 14 + /// The minimum length of a username. 15 + pub const MINIMUM_USERNAME_LENGTH: usize = 3; 16 + 17 + /// Checks if a username contains valid characters. 18 + /// 19 + /// # Arguments 20 + /// 21 + /// * `username` - A string slice that holds the username. 22 + /// 23 + /// # Returns 24 + /// 25 + /// * `bool` - Returns true if the username contains invalid characters, false otherwise. 26 + pub(crate) fn char_check_username(username: String) -> bool { 27 + username.chars().any(|c| { 28 + match c { 29 + ' ' | '\\' | '/' | '@' | '\n' | '\r' | '\t' | '\x0b' | '\'' | '"' | '(' | ')' | '`' 30 + | '%' | '?' | '!' => true, 31 + '#' => ( 32 + // Make sure, if a # is in the username, only 4 numbers may follow it. 33 + || { 34 + let split_username = username.split('#'); 35 + let array_split_username: Vec<&str> = split_username.collect(); 36 + let lastbit = username.replacen(array_split_username[0], "", 1); 37 + let firstbit = username.replacen(&*lastbit, "", 1); 38 + let vec_split_username: Vec<&str> = vec![&*firstbit, &*lastbit]; 39 + // println!("array: {:?}", array_split_username); 40 + // println!("vec: {:?}", vec_split_username); 41 + if vec_split_username.is_empty() || array_split_username[1].is_empty() { 42 + return true; 43 + }; 44 + (!array_split_username[1].chars().all(char::is_numeric)) 45 + || !(vec_split_username[1].len() == 5 || vec_split_username[1].len() == 7) 46 + } 47 + )(), 48 + _ => false, 49 + } 50 + }) || !username 51 + .replace(['_', '-', '.'], "") 52 + .replacen('#', "", 1) 53 + .chars() 54 + .all(char::is_alphanumeric) 55 + } 56 + 57 + /// Adds a new user to the database. 58 + /// 59 + /// # Arguments 60 + /// 61 + /// * `username` - A string slice that holds the username. 62 + /// * `email` - A string slice that holds the email. 63 + /// * `password` - A string slice that holds the password. 64 + /// * `config` - A reference to the LuminaConfig struct. 65 + /// 66 + /// # Returns 67 + /// 68 + /// * `Result<i64, Error>` - Returns the user id if the user is successfully added, otherwise returns an error. 69 + pub(crate) fn add( 70 + username: String, 71 + email: String, 72 + password: String, 73 + config: &LuminaConfig, 74 + ) -> Result<i64, Error> { 75 + if char_check_username(username.clone()) { 76 + return Err(Error::new( 77 + ErrorKind::Other, 78 + "Invalid characters in username!", 79 + )); 80 + } 81 + if username.len() < MINIMUM_USERNAME_LENGTH { 82 + return Err(Error::new(ErrorKind::Other, "Username is too short.")); 83 + } 84 + use regex::Regex; 85 + let email_regex = Regex::new( 86 + r"^([a-z0-9_+]([a-z0-9_+.]*[a-z0-9_+])?)@([a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,6})", 87 + ) 88 + .unwrap(); 89 + if !email_regex.is_match(&email) { 90 + return Err(Error::new(ErrorKind::Other, "Email is invalid.")); 91 + }; 92 + if password.len() < 8 { 93 + return Err(Error::new(ErrorKind::Other, "Password is too short.")); 94 + } 95 + let mcrypt = new_magic_crypt!(config.clone().db_custom_salt, 256); 96 + let conn = &config.clone(); 97 + let onusername = database::fetch::user(&config.clone(), ("username", username.clone()))?; 98 + let onemail = database::fetch::user(&config.clone(), ("email", email.clone()))?; 99 + let res: Option<User> = match onusername { 100 + Some(s) => Some(s), 101 + None => onemail, 102 + }; 103 + let password_encrypted = mcrypt.encrypt_str_to_base64(password); 104 + match res { 105 + Some(_) => Err(Error::new(ErrorKind::Other, "User already exists!")), 106 + None => { 107 + match conn.db_connect().execute3( 108 + "INSERT INTO `users` (username, password, email) VALUES (?1,?2,?3)", 109 + (username.clone(), password_encrypted, email), 110 + ) { 111 + Ok(_) => { 112 + match database::fetch::user(&config.clone(), ("username", username.clone()))? { 113 + Some(q) => Ok(q.id), 114 + None => Err(Error::new( 115 + ErrorKind::Other, 116 + "Unknown database check error.", 117 + )), 118 + } 119 + } 120 + Err(_) => { 121 + error!("Unknown database write error!"); 122 + Err(Error::new( 123 + ErrorKind::Other, 124 + "Unknown database write error.", 125 + )) 126 + } 127 + } 128 + } 129 + } 130 + } 131 + 132 + pub(crate) mod auth { 133 + use crate::database::users::User; 134 + use crate::ServerVars; 135 + use actix_web::web::Data; 136 + use colored::Colorize; 137 + use magic_crypt::{new_magic_crypt, MagicCryptTrait}; 138 + use tokio::sync::Mutex; 139 + 140 + /// # AuthResponse 141 + /// Tells the server what the database knows of a user. If it exists, and if the password provided was correct. 142 + pub enum AuthResponse { 143 + Success(i64), 144 + UserNoneExistant, 145 + Fail(FailReason), 146 + } 147 + /// # FailReason 148 + /// The reason why the authentication failed. 149 + pub enum FailReason { 150 + Unspecified, 151 + PasswordIncorrect, 152 + InvalidUsername, 153 + } 154 + 155 + /// # `storage::users::auth::check()` 156 + /// Authenticates a user by plain username/email and password. 157 + pub(crate) async fn check( 158 + identifyer: String, 159 + password: String, 160 + server_vars_mutex: &Data<Mutex<ServerVars>>, 161 + ) -> AuthResponse { 162 + let server_vars = ServerVars::grab(server_vars_mutex).await; 163 + if identifyer.chars().any(|c| { 164 + matches!( 165 + c, 166 + ' ' | '\\' | '/' | '\n' | '\r' | '\t' | '\x0b' | '\'' | '"' | '(' | ')' | '`' 167 + ) 168 + }) { 169 + // Invalid characters in username. 170 + return AuthResponse::Fail(FailReason::InvalidUsername); 171 + } 172 + let config: crate::LuminaConfig = server_vars.clone().config.clone(); 173 + let mcrypt = new_magic_crypt!(config.clone().db_custom_salt, 256); 174 + let errorresponse = |e| { 175 + error!("{}: \n\t\tRan into an error:\n {}", "Auth".purple(), e); 176 + AuthResponse::Fail(FailReason::Unspecified) 177 + }; 178 + let onusername = 179 + match super::database::fetch::user(&config.clone(), ("username", identifyer.clone())) { 180 + Ok(a) => a, 181 + Err(e) => return errorresponse(e), 182 + }; 183 + let onemail = 184 + match super::database::fetch::user(&config.clone(), ("email", identifyer.clone())) { 185 + Ok(a) => a, 186 + Err(e) => return errorresponse(e), 187 + }; 188 + let a_some: Option<User> = match onusername { 189 + Some(s) => Some(s), 190 + None => onemail, 191 + }; 192 + match a_some { 193 + Some(u) => { 194 + if u.password == mcrypt.encrypt_str_to_base64(password) { 195 + server_vars.tell(format!( 196 + "{}\t\t\t{}", 197 + "Auth".purple(), 198 + format!("User {} successfully authorised.", u.username.blue()).green() 199 + )); 200 + AuthResponse::Success(u.id) 201 + } else { 202 + server_vars.tell(format!( 203 + "{}\t\t\t{}", 204 + "Auth".purple(), 205 + format!("User {}: Wrong password entered.", identifyer.blue()).bright_red() 206 + )); 207 + AuthResponse::Fail(FailReason::PasswordIncorrect) 208 + } 209 + } 210 + None => { 211 + server_vars.tell(format!( 212 + "{}\t\t\t{}", 213 + "Auth".purple(), 214 + format!("User {} does not exist.", identifyer.blue()).bright_yellow() 215 + )); 216 + AuthResponse::UserNoneExistant 217 + } 218 + } 219 + } 220 + } 221 + 222 + #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 223 + #[serde(rename_all = "snake_case")] 224 + pub struct SafeUser { 225 + pub id: i64, 226 + pub username: String, 227 + pub email: String, 228 + } 229 + 230 + /// Basic user-identifying information. 231 + #[derive(Debug, Serialize, Deserialize)] 232 + pub struct User { 233 + /// User ID 234 + pub(crate) id: i64, 235 + /// Known username 236 + pub(crate) username: String, 237 + /// Hashed password 238 + pub(crate) password: String, 239 + /// Given email 240 + pub(crate) email: String, 241 + } 242 + 243 + impl User { 244 + pub fn to_exchangable(&self, config: &LuminaConfig) -> IIExchangedUserInfo { 245 + IIExchangedUserInfo { 246 + id: self.id, 247 + username: self.username.clone(), 248 + instance: config.lumina_synchronisation_iid.clone(), 249 + } 250 + } 251 + } 252 + 253 + impl DatabaseItem for User {}
+167
backend-rs/src/post.rs
··· 1 + /* 2 + * Copyright (c) 2024, MLC 'Strawmelonjuice' Bloeiman 3 + * 4 + * Licensed under the BSD 3-Clause License. See the LICENSE file for more info. 5 + */ 6 + 7 + use handlebars::*; 8 + use serde::{Deserialize, Serialize}; 9 + 10 + use crate::assets::STR_ASSETS_POST_RENDERS_HANDLEBARS; 11 + use crate::database::{unifetch, IIExchangedUserInfo}; 12 + use crate::database::users::User; 13 + use crate::LuminaConfig; 14 + 15 + #[derive(Debug, Serialize, Deserialize)] 16 + pub struct PostInfo { 17 + /// Local Post ID 18 + pub lpid: i64, 19 + /// Post ID 20 + pub pid: i64, 21 + /// Instance ID - Might not be necessary on local posts. Hence, being Option<>; 22 + pub instance: Option<String>, 23 + /// Author ID 24 + pub author_id: i64, 25 + /// Timestamp (UNIX) 26 + pub timestamp: i64, 27 + /// Content type 28 + // 1: Textual post (json<content: text/plain>) 29 + // 2: Article textual post (json<header: text/plain, body: text/markdown>) 30 + // 3: Media post (json<caption: text/markdown, media-id: integer[]>) 31 + // 4: Repost (json<repost-id: integer, source-instance: text>) 32 + pub content_type: i32, 33 + /// Content in JSON, deserialised depending on content_type. 34 + pub content: String, 35 + /// Tags 36 + pub tags: String, // Json<Vec<String>> 37 + } 38 + 39 + #[derive(Debug, Serialize, Deserialize)] 40 + pub struct PostPreRenderData { 41 + pub(crate) posttype: i32, 42 + pub(crate) author: IIExchangedUserInfo, 43 + pub(crate) content: String, 44 + pub(crate) timestamp: i64, 45 + pub(crate) title: Option<String>, 46 + pub(crate) embeds: Option<Vec<String>>, 47 + pub(crate) tags: Vec<String>, 48 + pub(crate) local: bool, 49 + button_push: String, 50 + button_comment: String, 51 + button_boost: String, 52 + } 53 + impl PostPreRenderData { 54 + pub fn to_html(&self) -> String { 55 + let mut handlebars = Handlebars::new(); 56 + handlebars_helper!(num_is_equal: |x: u64, y: u64| x == y); 57 + handlebars.register_helper("numequal", Box::new(num_is_equal)); 58 + 59 + handlebars 60 + .render_template(STR_ASSETS_POST_RENDERS_HANDLEBARS, self) 61 + .unwrap_or_else(|e| { 62 + eprintln!("Error rendering post: {}", e); 63 + String::from("Error rendering post.") 64 + }) 65 + } 66 + } 67 + 68 + impl PostInfo { 69 + pub fn to_formatted(&self, config: &LuminaConfig) -> PostPreRenderData { 70 + let author_u: User = 71 + unifetch::<User>(config, ("id", self.author_id.to_string().as_str())) 72 + .unwrap_user(); 73 + 74 + let author = IIExchangedUserInfo { 75 + id: self.author_id, 76 + username: author_u.username, 77 + instance: self 78 + .instance 79 + .clone() 80 + .unwrap_or(config.lumina_synchronisation_iid.clone()), 81 + }; 82 + let content; 83 + 84 + let mut embeds = None; 85 + 86 + let mut title = None; 87 + 88 + match self.content_type { 89 + 1 => { 90 + #[derive(Serialize, Deserialize)] 91 + struct TextPost { 92 + content: String, 93 + } 94 + 95 + content = serde_json::from_str::<TextPost>(&self.content) 96 + .unwrap() 97 + .content; 98 + } 99 + 2 => { 100 + #[derive(Serialize, Deserialize)] 101 + struct ArticlePost { 102 + header: String, 103 + body: String, 104 + } 105 + 106 + let article = serde_json::from_str::<ArticlePost>(&self.content).unwrap(); 107 + content = markdown::to_html(&article.body); 108 + title = Some(article.header); 109 + } 110 + 3 => { 111 + #[derive(Serialize, Deserialize)] 112 + struct MediaPost { 113 + caption: String, 114 + media_id: Vec<i64>, 115 + } 116 + 117 + let media = serde_json::from_str::<MediaPost>(&self.content).unwrap(); 118 + content = markdown::to_html(&media.caption); 119 + embeds = Some(media.media_id.iter().map(|x| x.to_string()).collect()); 120 + } 121 + 4 => { 122 + #[derive(Serialize, Deserialize)] 123 + struct Repost { 124 + repost_id: i64, 125 + source_instance: String, 126 + } 127 + 128 + let repost = serde_json::from_str::<Repost>(&self.content).unwrap(); 129 + content = format!( 130 + "<a href=\"{}/post/{}\">Reposted from {}</a>", 131 + repost.source_instance, repost.repost_id, repost.source_instance 132 + ); 133 + } 134 + _ => { 135 + error!("Unknown content type!"); 136 + panic!("Unknown content type!"); 137 + } 138 + }; 139 + 140 + let timestamp = self.timestamp; 141 + 142 + let tags = serde_json::from_str::<Vec<String>>(&self.tags).unwrap(); 143 + 144 + let local = self.instance.is_none(); 145 + 146 + PostPreRenderData { 147 + posttype: self.content_type, 148 + author, 149 + content, 150 + timestamp, 151 + title, 152 + embeds, 153 + tags, 154 + local, 155 + button_push: crate::assets::STR_ASSETS_BTN_PUSH_SVG 156 + .replace(r#"height="120""#, r#"height="100%""#) 157 + .replace(r#"width="120""#, r#"width="100%""#), 158 + // <svg width="100%" height="100%" viewBox="0 0 120 120" xmlns="http://www.w3.org/2000/svg"> 159 + button_comment: crate::assets::STR_ASSETS_BTN_COMMENT_SVG 160 + .replace(r#"height="120""#, r#"height="100%""#) 161 + .replace(r#"width="120""#, r#"width="100%""#), 162 + button_boost: crate::assets::STR_ASSETS_BTN_BOOST_SVG 163 + .replace(r#"height="120""#, r#"height="100%""#) 164 + .replace(r#"width="120""#, r#"width="100%""#), 165 + } 166 + } 167 + }
+497
backend-rs/src/serve.rs
··· 1 + /* 2 + * Copyright (c) 2024, MLC 'Strawmelonjuice' Bloeiman 3 + * 4 + * Licensed under the BSD 3-Clause License. See the LICENSE file for more info. 5 + */ 6 + const SPECIALDATES: &str = r#" 7 + /*Pride month banner*/ 8 + body:has(.monthclass-6)::before { 9 + margin: 0; 10 + content: "Happy Pride Month! 💖🏳️‍🌈"; 11 + justify-content: center; 12 + align-items: center; 13 + height: 1.4em; 14 + color: black; 15 + width: 100VW; 16 + border-radius: 0; 17 + display: inline-flex; 18 + background-image: linear-gradient(to right, rgb(237, 34, 36), rgb(243, 91, 34), rgb(249, 150, 33), rgb(245, 193, 30), rgb(241, 235, 27) 27%, rgb(241, 235, 27), rgb(241, 235, 27) 33%, rgb(99, 199, 32), rgb(12, 155, 73), rgb(33, 135, 141), rgb(57, 84, 165), rgb(97, 55, 155), rgb(147, 40, 142)) 19 + } 20 + 21 + 22 + 23 + body:has(.monthclass-6) { 24 + --bs: 300% 100%; 25 + 26 + } 27 + body:has(.monthclass-6):hover::before { 28 + animation: prideBannerAnimation 10s linear infinite; 29 + } 30 + @keyframes prideBannerAnimation { 31 + 0% { } 32 + 25% { background-position: 0 0; 33 + background-size: var(--bs); 34 + background-repeat: repeat;} 35 + 30% { background-position: 50% 0; 36 + content: "Protect LGBTQ+ Rights! 🏳️‍🌈✊"; 37 + background-size: var(--bs); 38 + background-repeat: repeat; 39 + } 40 + 50% { background-position: 100% 0; 41 + content: "Protect LGBTQ+ Rights! 🏳️‍🌈✊"; 42 + background-size: var(--bs); 43 + background-repeat: repeat; 44 + } 45 + 46 + 75% { background-position: 0 0; 47 + background-size: var(--bs); 48 + background-repeat: repeat; 49 + } 50 + 80% { background-position: 50% 0; 51 + content: "Protect LGBTQ+ Rights! 🏳️‍🌈 ✊"; 52 + background-size: var(--bs); 53 + background-repeat: repeat; 54 + } 55 + 100% { } 56 + } 57 + body:has(.monthclass-6):active::before { 58 + animation: none; 59 + animation-delay: 3s; 60 + animation-duration: 999s; 61 + animation-name: transrights; 62 + animation-iteration-count: 1; 63 + animation-timing-function: ease-in-out; 64 + } 65 + @keyframes transrights { 66 + 0% { 67 + content: "Protect trans Rights! ✊ 🩵🩷🤍🩷🩵"; 68 + background-image: linear-gradient(to right, rgb(85, 205, 252), rgb(179, 157, 233), rgb(247, 168, 184), rgb(246, 216, 221), rgb(255, 255, 255) 45%, rgb(255, 255, 255), rgb(255, 255, 255) 55%, rgb(246, 216, 221), rgb(247, 168, 184), rgb(179, 157, 233), rgb(85, 205, 252)); 69 + } 70 + } 71 + /*29th of februari is nonexistent in non-leap years*/ 72 + body:has(.dayclass-29.monthclass-2)::before { 73 + margin-top: .8em; 74 + margin-bottom: .8em; 75 + content: "[This day does not exist]"; 76 + justify-content: center; 77 + align-items: center; 78 + height: 2.4em; 79 + flex: none; 80 + color: yellow; 81 + width: 100%; 82 + display: inline-flex; 83 + background-color: black; 84 + text-shadow: 22px 4px 2px rgba(255,255,0,0.6); 85 + box-shadow: 2px 2px 10px 8px #3d3a3a; 86 + animation-name: glitched; 87 + animation-duration: 3s; 88 + animation-iteration-count: infinite; 89 + animation-timing-function: linear; 90 + animation-direction: alternate; 91 + } 92 + @keyframes glitched { 93 + 0% { 94 + transform: skew(-20deg); 95 + left: -4px; 96 + } 97 + 10% { 98 + transform: skew(-20deg); 99 + left: -4px; 100 + } 101 + 11% { 102 + transform: skew(0deg); 103 + left: 2px; 104 + } 105 + 50% { 106 + transform: skew(0deg); 107 + } 108 + 51% { 109 + transform: skew(10deg); 110 + } 111 + 59% { 112 + transform: skew(10deg); 113 + } 114 + 60% { 115 + transform: skew(0deg); 116 + } 117 + 100% { 118 + transform: skew(0deg); 119 + } 120 + }"#; 121 + 122 + use actix_session::Session; 123 + use actix_web::http::header::LOCATION; 124 + use actix_web::http::StatusCode; 125 + use actix_web::web::Data; 126 + use actix_web::{HttpRequest, HttpResponse, Responder}; 127 + use colored::Colorize; 128 + use tokio::sync::{Mutex, MutexGuard}; 129 + 130 + use crate::database::users::User; 131 + use crate::database::{self, IIExchangedUserInfo}; 132 + use crate::{LuminaConfig, ServerVars}; 133 + use chrono::Datelike; 134 + 135 + pub(super) async fn notfound( 136 + server_vars_mutex: Data<Mutex<ServerVars>>, 137 + req: HttpRequest, 138 + session: Session, 139 + ) -> HttpResponse { 140 + let _server_vars = ServerVars::grab(&server_vars_mutex).await; 141 + let username_ = session.get::<String>("username"); 142 + let username = username_.unwrap_or(None).unwrap_or(String::from("")); 143 + let username_b = if username != *"" { 144 + format!("/{}", username.green()) 145 + } else { 146 + String::from("") 147 + }; 148 + let coninfo = req.connection_info(); 149 + let ip = coninfo.realip_remote_addr().unwrap_or("<unknown IP>"); 150 + 151 + warn!( 152 + "{}\t{:>45.47}\t\t{}{:<26}", 153 + "Request/404".bright_red(), 154 + req.path().red(), 155 + ip.yellow(), 156 + username_b 157 + ); 158 + HttpResponse::NotFound().body("") 159 + } 160 + 161 + pub(super) async fn root( 162 + server_vars_mutex: Data<Mutex<ServerVars>>, 163 + req: HttpRequest, 164 + ) -> HttpResponse { 165 + let server_vars: ServerVars = ServerVars::grab(&server_vars_mutex).await; 166 + let coninfo = req.connection_info(); 167 + let ip = coninfo.realip_remote_addr().unwrap_or("<unknown IP>"); 168 + server_vars.tell(format!( 169 + "{2}\t{:>45.47}\t\t{}", 170 + "/".bright_magenta(), 171 + ip.yellow(), 172 + "Request/200".bright_green() 173 + )); 174 + 175 + HttpResponse::build(StatusCode::OK) 176 + .content_type("text/html; charset=utf-8") 177 + .body(replaceable( 178 + crate::assets::STR_ASSETS_INDEX_HTML, 179 + &server_vars, 180 + )) 181 + } 182 + 183 + fn replaceable(string: &str, server_vars: &ServerVars) -> String { 184 + // "Compress" that CSS too, however, be careful. Two consecutive spaces can be removed, but one on its own might mean something! 185 + let specialdates = SPECIALDATES.replace(['\r', '\n'], "").replace(" ", ""); 186 + let current_date = chrono::Utc::now(); 187 + let mut stylesheet: String = String::new(); 188 + stylesheet.push_str("<style>"); 189 + stylesheet.push_str("\n\n\n/* --- Main stylesheet --- */\n\n\n"); 190 + stylesheet.push_str(crate::assets::STR_GENERATED_MAIN_MIN_CSS); 191 + stylesheet.push_str("\n\n\n/* --- Custom instance-specific CSS content --- */\n\n\n"); 192 + stylesheet.push_str(&server_vars.config.clone().erun.customcss.clone()); 193 + stylesheet.push_str("\n\n\n/* --- CSS content for special events --- */\n\n\n"); 194 + stylesheet.push_str(specialdates.as_str()); 195 + stylesheet.push_str("</style>"); 196 + let s = string 197 + .replace( 198 + "{{iid}}", 199 + &server_vars 200 + .clone() 201 + .config 202 + .lumina_synchronisation_iid 203 + .clone(), 204 + ) 205 + .replace( 206 + "monthclass-month", 207 + format!("monthclass-{}", current_date.month()).as_str(), 208 + ) 209 + .replace( 210 + "dayclass-day", 211 + format!("dayclass-{}", current_date.day()).as_str(), 212 + ) 213 + .replace("<style></style>", &stylesheet); 214 + s.clone() 215 + } 216 + 217 + pub(super) async fn login( 218 + server_vars_mutex: Data<Mutex<ServerVars>>, 219 + req: HttpRequest, 220 + ) -> HttpResponse { 221 + let server_vars: ServerVars = ServerVars::grab(&server_vars_mutex).await; 222 + let coninfo = req.connection_info(); 223 + let ip = coninfo.realip_remote_addr().unwrap_or("<unknown IP>"); 224 + server_vars.tell(format!( 225 + "{2}\t{:>45.47}\t\t{}", 226 + "/login".bright_magenta(), 227 + ip.yellow(), 228 + "Request/200".bright_green() 229 + )); 230 + 231 + HttpResponse::build(StatusCode::OK) 232 + .content_type("text/html; charset=utf-8") 233 + .body(replaceable( 234 + crate::assets::STR_ASSETS_LOGIN_HTML, 235 + &server_vars, 236 + )) 237 + } 238 + 239 + pub(super) async fn signup( 240 + server_vars_mutex: Data<Mutex<ServerVars>>, 241 + req: HttpRequest, 242 + ) -> HttpResponse { 243 + let server_vars: ServerVars = ServerVars::grab(&server_vars_mutex).await; 244 + let coninfo = req.connection_info(); 245 + let ip = coninfo.realip_remote_addr().unwrap_or("<unknown IP>"); 246 + server_vars.tell(format!( 247 + "{2}\t{:>45.47}\t\t{}", 248 + "/signup".bright_magenta(), 249 + ip.yellow(), 250 + "Request/200".bright_green() 251 + )); 252 + 253 + HttpResponse::build(StatusCode::OK) 254 + .content_type("text/html; charset=utf-8") 255 + .body(replaceable( 256 + crate::assets::STR_ASSETS_SIGNUP_HTML, 257 + &server_vars, 258 + )) 259 + } 260 + 261 + pub(super) async fn appjs( 262 + server_vars_mutex: Data<Mutex<ServerVars>>, 263 + req: HttpRequest, 264 + ) -> HttpResponse { 265 + let server_vars = ServerVars::grab(&server_vars_mutex).await; 266 + let coninfo = req.connection_info(); 267 + let ip = coninfo.realip_remote_addr().unwrap_or("<unknown IP>"); 268 + server_vars.tell(format!( 269 + "{2}\t{:>45.47}\t\t{}", 270 + "/app.js".magenta(), 271 + ip.yellow(), 272 + "Request/200".bright_green() 273 + )); 274 + HttpResponse::build(StatusCode::OK) 275 + .content_type("text/javascript; charset=utf-8") 276 + .body(crate::assets::STR_ASSETS_APPJS) 277 + } 278 + 279 + pub(super) async fn appjsmap( 280 + server_vars_mutex: Data<Mutex<ServerVars>>, 281 + req: HttpRequest, 282 + ) -> HttpResponse { 283 + let server_vars = ServerVars::grab(&server_vars_mutex).await; 284 + let coninfo = req.connection_info(); 285 + let ip = coninfo.realip_remote_addr().unwrap_or("<unknown IP>"); 286 + server_vars.tell(format!( 287 + "{2}\t{:>45.47}\t\t{}", 288 + "/app.js.map".magenta(), 289 + ip.yellow(), 290 + "Request/200".bright_green() 291 + )); 292 + HttpResponse::build(StatusCode::OK) 293 + .content_type("text/javascript; charset=utf-8") 294 + .body(crate::assets::STR_ASSETS_APPJS_MAP) 295 + } 296 + 297 + pub(super) async fn red_cross_svg( 298 + server_vars_mutex: Data<Mutex<ServerVars>>, 299 + req: HttpRequest, 300 + ) -> HttpResponse { 301 + let server_vars = ServerVars::grab(&server_vars_mutex).await; 302 + let coninfo = req.connection_info(); 303 + let ip = coninfo.realip_remote_addr().unwrap_or("<unknown IP>"); 304 + server_vars.tell(format!( 305 + "{2}\t{:>45.47}\t\t{}", 306 + "/red-cross.svg".magenta(), 307 + ip.yellow(), 308 + "Request/200".bright_green() 309 + )); 310 + HttpResponse::build(StatusCode::OK) 311 + .content_type("image/svg+xml; charset=utf-8") 312 + .body(crate::assets::STR_ASSETS_RED_CROSS_SVG) 313 + } 314 + 315 + pub(super) async fn spinner_svg( 316 + server_vars_mutex: Data<Mutex<ServerVars>>, 317 + req: HttpRequest, 318 + ) -> HttpResponse { 319 + let server_vars = ServerVars::grab(&server_vars_mutex).await; 320 + let coninfo = req.connection_info(); 321 + let ip = coninfo.realip_remote_addr().unwrap_or("<unknown IP>"); 322 + server_vars.tell(format!( 323 + "{2}\t{:>45.47}\t\t{}", 324 + "/spinner.svg".magenta(), 325 + ip.yellow(), 326 + "Request/200".bright_green() 327 + )); 328 + HttpResponse::build(StatusCode::OK) 329 + .content_type("image/svg+xml; charset=utf-8") 330 + .body(crate::assets::STR_ASSETS_SPINNER_SVG) 331 + } 332 + 333 + pub(super) async fn green_check_svg( 334 + server_vars_mutex: Data<Mutex<ServerVars>>, 335 + req: HttpRequest, 336 + ) -> HttpResponse { 337 + let server_vars = ServerVars::grab(&server_vars_mutex).await; 338 + let coninfo = req.connection_info(); 339 + let ip = coninfo.realip_remote_addr().unwrap_or("<unknown IP>"); 340 + server_vars.tell(format!( 341 + "{2}\t{:>45.47}\t\t{}", 342 + "/green-check.svg".magenta(), 343 + ip.yellow(), 344 + "Request/200".bright_green() 345 + )); 346 + HttpResponse::build(StatusCode::OK) 347 + .content_type("image/svg+xml; charset=utf-8") 348 + .body(crate::assets::STR_ASSETS_GREEN_CHECK_SVG) 349 + } 350 + 351 + pub(super) async fn logo_svg( 352 + server_vars_mutex: Data<Mutex<ServerVars>>, 353 + req: HttpRequest, 354 + ) -> HttpResponse { 355 + let server_vars = ServerVars::grab(&server_vars_mutex).await; 356 + let coninfo = req.connection_info(); 357 + let ip = coninfo.realip_remote_addr().unwrap_or("<unknown IP>"); 358 + server_vars.tell(format!( 359 + "{2}\t{:>45.47}\t\t{}", 360 + "/logo.svg".magenta(), 361 + ip.yellow(), 362 + "Request/200".bright_green() 363 + )); 364 + HttpResponse::build(StatusCode::OK) 365 + .content_type("image/svg+xml; charset=utf-8") 366 + .body(crate::assets::STR_ASSETS_LOGO_SVG) 367 + } 368 + 369 + pub(super) async fn logo_png( 370 + server_vars_mutex: Data<Mutex<ServerVars>>, 371 + req: HttpRequest, 372 + ) -> HttpResponse { 373 + let server_vars = ServerVars::grab(&server_vars_mutex).await; 374 + let coninfo = req.connection_info(); 375 + let ip = coninfo.realip_remote_addr().unwrap_or("<unknown IP>"); 376 + server_vars.tell(format!( 377 + "{2}\t{:>45.47}\t\t{}", 378 + "/logo.png".magenta(), 379 + ip.yellow(), 380 + "Request/200".bright_green() 381 + )); 382 + HttpResponse::build(StatusCode::OK) 383 + .content_type("image/png; charset=utf-8") 384 + .body(crate::assets::BYTES_ASSETS_LOGO_PNG) 385 + } 386 + 387 + pub(super) async fn homepage( 388 + server_vars_mutex: Data<Mutex<ServerVars>>, 389 + session: Session, 390 + req: HttpRequest, 391 + ) -> HttpResponse { 392 + fence( 393 + session, 394 + server_vars_mutex, 395 + req, 396 + |_, server_vars, user, request| { 397 + let coninfo = request.connection_info(); 398 + let ip = coninfo.realip_remote_addr().unwrap_or("<unknown IP>"); 399 + server_vars.tell(format!( 400 + "{}\t{:>45.47}\t\t{}/{:<25}", 401 + "Request/200".bright_green(), 402 + "/home".bright_magenta(), 403 + ip.yellow(), 404 + user.username.green() 405 + )); 406 + HttpResponse::build(StatusCode::OK) 407 + .content_type("text/html; charset=utf-8") 408 + .body(replaceable( 409 + crate::assets::STR_ASSETS_HOME_HTML, 410 + &server_vars, 411 + )) 412 + }, 413 + ) 414 + .await 415 + } 416 + 417 + pub(super) async fn logout( 418 + server_vars_mutex: Data<Mutex<ServerVars>>, 419 + session: Session, 420 + req: HttpRequest, 421 + ) -> impl Responder { 422 + let server_vars = ServerVars::grab(&server_vars_mutex).await; 423 + let username_ = session.get::<String>("username"); 424 + let coninfo = req.connection_info(); 425 + let ip = coninfo.realip_remote_addr().unwrap_or("<unknown IP>"); 426 + match username_.unwrap_or(None) { 427 + Some(username) => { 428 + server_vars.tell(format!( 429 + "{}\t{:>45.47}\t\t{}/{:<25}", 430 + "Request/200".bright_green(), 431 + "/session/logout".bright_magenta(), 432 + ip.yellow(), 433 + username.green() 434 + )); 435 + session.purge(); 436 + HttpResponse::build(StatusCode::TEMPORARY_REDIRECT) 437 + .append_header((LOCATION, "/login")) 438 + .finish() 439 + } 440 + None => HttpResponse::build(StatusCode::TEMPORARY_REDIRECT) 441 + .append_header((LOCATION, "/login")) 442 + .finish(), 443 + } 444 + } 445 + 446 + /// Fence is a function serving kind of like middleware usually would. But actix middleware sucks balls. 447 + /// 448 + /// This function checks the validity of the user session. If the session is invalid, it purges the session and redirects to the login page. 449 + /// If the session is valid, it fetches the user information from the database and calls the next function in the chain with the necessary parameters. 450 + /// 451 + /// # Arguments 452 + /// 453 + /// * `session` - The current user session. 454 + /// * `server_vars_mutex` - A mutex guarding the server variables. 455 + /// * `req` - The incoming HTTP request. 456 + /// * `next` - The next function to call if the session is valid. 457 + /// 458 + /// # Returns 459 + /// 460 + /// An `HttpResponse` indicating the result of the session validation and the next function call. 461 + pub(crate) async fn fence( 462 + session: Session, 463 + server_vars_mutex: Data<Mutex<ServerVars>>, 464 + req: HttpRequest, 465 + next: fn( 466 + config: LuminaConfig, 467 + vars: ServerVars, 468 + user: IIExchangedUserInfo, 469 + req: HttpRequest, 470 + ) -> HttpResponse, 471 + ) -> HttpResponse { 472 + let server_vars: MutexGuard<ServerVars> = server_vars_mutex.lock().await; 473 + let config = server_vars.clone().config; 474 + let id_ = session.get::<i64>("userid").unwrap_or(Some(-100)); 475 + let id = id_.unwrap_or(-100); 476 + debug!("Session validity: {:?}", session.get::<i64>("validity")); 477 + debug!("Session contents: {:?}", session.entries()); 478 + debug!("User ID: {:?}", id); 479 + let safe = crate::api_fe::checksessionvalidity(id, &session, &config); 480 + if !safe { 481 + session.purge(); 482 + HttpResponse::build(StatusCode::TEMPORARY_REDIRECT) 483 + .append_header((LOCATION, "/login")) 484 + .finish() 485 + } else { 486 + let user: User = database::fetch::user(&config, ("id", id.to_string())) 487 + .unwrap() 488 + .unwrap(); 489 + 490 + next( 491 + config.clone(), 492 + server_vars.clone().to_owned(), 493 + user.to_exchangable(&config), 494 + req, 495 + ) 496 + } 497 + }
+486
backend-rs/src/server.rs
··· 1 + /* 2 + * Copyright (c) 2024, MLC 'Strawmelonjuice' Bloeiman 3 + * 4 + * Licensed under the BSD 3-Clause License. See the LICENSE file for more info. 5 + */ 6 + 7 + #[macro_use] 8 + extern crate log; 9 + extern crate simplelog; 10 + 11 + use std::fmt::Debug; 12 + use std::fs::File; 13 + use std::io::Write; 14 + use std::path::PathBuf; 15 + use std::{env, fs, path::Path, process}; 16 + 17 + use actix_session::storage::CookieSessionStore; 18 + use actix_session::{Session, SessionMiddleware}; 19 + use actix_web::cookie::Key; 20 + use actix_web::{get, HttpRequest, HttpResponse}; 21 + use actix_web::{ 22 + web::{self, Data}, 23 + App, HttpServer, 24 + }; 25 + use colored::Colorize; 26 + use rand::prelude::*; 27 + use simplelog::*; 28 + use tokio::sync::{Mutex, MutexGuard}; 29 + 30 + use crate::config::{LuminaConfig, LuminaLogConfig}; 31 + use crate::serve::notfound; 32 + use assets::{fonts, vec_string_assets_anons_svg, STR_CLEAN_CUSTOMSTYLES_CSS}; 33 + 34 + /// ## API's to the front-end. 35 + mod api_fe; 36 + /// # Inter-instance API's 37 + mod api_ii; 38 + /// ## Definition of assets, so file paths refactoring goes easier. 39 + pub mod assets; 40 + /// # Actions on the database 41 + mod database; 42 + /// # Actions on posts 43 + mod post; 44 + /// # Actions for gentle logging ("telling") 45 + /// Logging doesn't need this, but for prettyness these are added as implementations on ServerVars. 46 + mod tell; 47 + 48 + #[derive(Clone)] 49 + struct ServerVars { 50 + config: crate::config::LuminaConfig, 51 + } 52 + 53 + #[derive(Clone)] 54 + pub struct SynclistItem { 55 + pub name: String, // The name of the instance to sync with, equal to the domain name it is public on. 56 + pub level: String, // The level of syncing to do. "full" is the only one being implemented right now. 57 + pub last_contact: i64, // The last time the instance was contacted. 58 + } 59 + impl ServerVars { 60 + /// This function grabs the server variables from the provided mutex. 61 + /// 62 + /// # Arguments 63 + /// 64 + /// * `server_vars_mutex` - A reference to a `Data<Mutex<ServerVars>>` containing the server variables. 65 + /// 66 + /// # Returns 67 + /// 68 + /// * `ServerVars` - A clone of the server variables stored in the mutex. These are cloned so that the mutex can be unlocked without having to wait for the calling function to finish. 69 + /// 70 + /// # Errors 71 + /// 72 + /// This function does not return any errors. 73 + /// 74 + /// # Panics 75 + /// 76 + /// This function does not panic. 77 + /// 78 + /// # Examples 79 + /// 80 + /// ``` 81 + /// // This is the shield function from 'api_fe', it implements grab in the simplest way. 82 + /// async fn shield( 83 + /// session: Session, 84 + /// server_vars_mutex: &Data<Mutex<ServerVars>>, 85 + /// halt: HttpResponse, 86 + /// ) -> Option<HttpResponse> { 87 + /// let server_vars = ServerVars::grab(server_vars_mutex).await; 88 + /// let config = server_vars.clone().config; 89 + /// ... 90 + /// ``` 91 + pub(crate) async fn grab(server_vars_mutex: &Data<Mutex<ServerVars>>) -> ServerVars { 92 + let vars: MutexGuard<ServerVars> = server_vars_mutex.lock().await; 93 + vars.clone() 94 + } 95 + } 96 + 97 + #[derive(Default, Debug, Clone, PartialEq)] 98 + pub struct ERun { 99 + pub cd: PathBuf, 100 + pub customcss: String, 101 + pub session_valid: i64, 102 + } 103 + 104 + pub struct LogSets { 105 + pub file_loglevel: LevelFilter, 106 + pub term_loglevel: LevelFilter, 107 + pub logfile: PathBuf, 108 + } 109 + 110 + #[tokio::main] 111 + async fn main() { 112 + let v = (|| { 113 + if env::args().nth(1).unwrap_or(String::from("")) != *"" { 114 + return PathBuf::from(env::args().nth(1).unwrap()); 115 + }; 116 + match home::home_dir() { 117 + Some(path) => path.join(".luminainstance/"), 118 + None => PathBuf::from(Path::new(".")), 119 + } 120 + })(); 121 + 122 + let vs = v 123 + .canonicalize() 124 + .unwrap_or(v.to_path_buf()) 125 + .to_string_lossy() 126 + .replace("\\\\?\\", "") 127 + .to_string(); 128 + if !v.exists() { 129 + match fs::create_dir_all(v.clone()) { 130 + Ok(_) => {} 131 + Err(e) => { 132 + eprintln!( 133 + "Could not write necessary files! Error: {}", 134 + e.to_string().bright_red() 135 + ); 136 + process::exit(1); 137 + } 138 + } 139 + } 140 + if !v.is_dir() { 141 + eprintln!( 142 + "Unable to load or write config! Error: {}", 143 + format!("`{}` is not a directory.", vs).bright_red() 144 + ); 145 + process::exit(1); 146 + } 147 + 148 + let erun: ERun = { 149 + let sty_f = v.clone().join("./custom-styles.css"); 150 + if (!sty_f.is_file()) || (!sty_f.exists()) { 151 + let mut output = match File::create(sty_f.clone()) { 152 + Ok(p) => p, 153 + Err(a) => { 154 + eprintln!( 155 + "Error: Could not create blank style customisation file. The system returned: {}", 156 + a 157 + ); 158 + process::exit(1); 159 + } 160 + }; 161 + 162 + match write!(output, "{}", STR_CLEAN_CUSTOMSTYLES_CSS) { 163 + Ok(p) => p, 164 + Err(a) => { 165 + eprintln!( 166 + "Error: Could not create blank style customisation file. The system returned: {}", 167 + a 168 + ); 169 + process::exit(1); 170 + } 171 + }; 172 + }; 173 + // read the styf file to a string 174 + let styf = match fs::read_to_string(sty_f.clone()) { 175 + Ok(p) => p, 176 + Err(a) => { 177 + eprintln!( 178 + "Error: Could not read custom style file. The system returned: {}", 179 + a 180 + ); 181 + process::exit(1); 182 + } 183 + }; 184 + 185 + ERun { 186 + cd: v.clone(), 187 + customcss: styf, 188 + session_valid: rand::thread_rng().gen_range(0..1000000), 189 + } 190 + }; 191 + let logsets: LogSets = { 192 + fn matchlogmode(o: u8) -> LevelFilter { 193 + match o { 194 + 0 => LevelFilter::Off, 195 + 1 => LevelFilter::Error, 196 + 2 => LevelFilter::Warn, 197 + 3 => LevelFilter::Info, 198 + 4 => LevelFilter::Debug, 199 + 5 => LevelFilter::Trace, 200 + _ => { 201 + eprintln!( 202 + "{} Could not set loglevel `{}`! Ranges are 0-5 (quiet to verbose)", 203 + "error:".red(), 204 + o 205 + ); 206 + process::exit(1); 207 + } 208 + } 209 + } 210 + let temp: Option<LuminaLogConfig> = None; 211 + match temp { 212 + None => LogSets { 213 + file_loglevel: LevelFilter::Info, 214 + term_loglevel: LevelFilter::Warn, 215 + logfile: erun.cd.join("./instance.log"), 216 + }, 217 + Some(d) => LogSets { 218 + file_loglevel: match d.file_loglevel { 219 + Some(l) => matchlogmode(l), 220 + None => LevelFilter::Info, 221 + }, 222 + term_loglevel: match d.term_loglevel { 223 + Some(l) => matchlogmode(l), 224 + None => LevelFilter::Warn, 225 + }, 226 + logfile: match d.logfile { 227 + Some(s) => erun.cd.join(s.as_str()), 228 + None => erun.cd.join("./instance.log"), 229 + }, 230 + }, 231 + } 232 + }; 233 + CombinedLogger::init(vec![ 234 + TermLogger::new( 235 + logsets.term_loglevel, 236 + Config::default(), 237 + TerminalMode::Mixed, 238 + ColorChoice::Auto, 239 + ), 240 + WriteLogger::new( 241 + logsets.file_loglevel, 242 + Config::default(), 243 + File::create(&logsets.logfile).unwrap(), 244 + ), 245 + ]) 246 + .unwrap(); 247 + 248 + let config: LuminaConfig = LuminaConfig::new(erun.clone()); 249 + 250 + let server_p: ServerVars = ServerVars { 251 + config: config.clone(), 252 + }; 253 + let server_q: Data<Mutex<ServerVars>> = Data::new(Mutex::new(server_p.clone())); 254 + server_p.tell(format!( 255 + "Logging to {}", 256 + logsets 257 + .logfile 258 + .canonicalize() 259 + .unwrap() 260 + .to_string_lossy() 261 + .replace("\\\\?\\", "") 262 + )); 263 + config.db_connect().initial_dbconf(); 264 + let keydouble = config.db_custom_salt.repeat(10); 265 + let keybytes = keydouble.as_bytes(); 266 + if keybytes.len() < 32 { 267 + error!( 268 + "Error: Cookie key must be at least 32 (doubled) bytes long. \"{}\" yields only {} bytes.", 269 + config.db_custom_salt.blue(), 270 + format!("{}",keybytes.len()).blue() 271 + ); 272 + process::exit(1); 273 + } 274 + let secret_key: Key = Key::from(keybytes); 275 + let main_server = match HttpServer::new(move || { 276 + App::new() 277 + .wrap(SessionMiddleware::new( 278 + CookieSessionStore::default(), 279 + secret_key.clone(), 280 + )) 281 + .default_service(web::to(notfound)) 282 + .route("/", web::get().to(serve::root)) 283 + .route("/home", web::get().to(serve::homepage)) 284 + .route("/login", web::get().to(serve::login)) 285 + .route("/signup", web::get().to(serve::signup)) 286 + .route("/session/logout", web::get().to(serve::logout)) 287 + .route("/home/", web::get().to(serve::homepage)) 288 + .route("/login/", web::get().to(serve::login)) 289 + .route("/signup/", web::get().to(serve::signup)) 290 + .route("/session/logout/", web::get().to(serve::logout)) 291 + .route("/app.js", web::get().to(serve::appjs)) 292 + .route("/app.js.map", web::get().to(serve::appjsmap)) 293 + .route( 294 + "/api/fe/fetch-page", 295 + web::post().to(api_fe::pageservresponder), 296 + ) 297 + .route( 298 + "/api/fe/editor_fetch_markdownpreview", 299 + web::post().to(api_fe::render_editor_articlepost), 300 + ) 301 + .route("/api/fe/update", web::get().to(api_fe::update)) 302 + .route("/api/fe/auth/", web::post().to(api_fe::auth)) 303 + .route("/api/fe/auth", web::post().to(api_fe::auth)) 304 + .route("/api/fe/auth-create/", web::post().to(api_fe::newaccount)) 305 + .route("/api/fe/auth-create", web::post().to(api_fe::newaccount)) 306 + .route( 307 + "/api/fe/auth-create/check-username", 308 + web::post().to(api_fe::check_username), 309 + ) 310 + .route("/red-cross.svg", web::get().to(serve::red_cross_svg)) 311 + .route("/spinner.svg", web::get().to(serve::spinner_svg)) 312 + .route("/green-check.svg", web::get().to(serve::green_check_svg)) 313 + .route("/logo.svg", web::get().to(serve::logo_svg)) 314 + .route("/favicon.ico", web::get().to(serve::logo_png)) 315 + .route("/logo.png", web::get().to(serve::logo_png)) 316 + .service(avatar) 317 + .service(serve_fonts) 318 + .app_data(web::Data::clone(&server_q)) 319 + }) 320 + .bind((config.lumina_server_addr.clone(), config.lumina_server_port)) 321 + { 322 + Ok(o) => { 323 + server_p.tell(format!( 324 + "Running on http://{0}:{1}, which should be bound to {2}://{3}", 325 + config.lumina_server_addr, 326 + config.lumina_server_port, 327 + if config.lumina_server_https { 328 + "https" 329 + } else { 330 + "http" 331 + }, 332 + config.lumina_synchronisation_iid 333 + )); 334 + o 335 + } 336 + Err(s) => { 337 + error!( 338 + "Could not bind to {}:{}, error message: {}", 339 + config.lumina_server_addr, config.lumina_server_port, s 340 + ); 341 + process::exit(1); 342 + } 343 + } 344 + .run(); 345 + let _ = futures::join!(api_ii::main(server_p.clone()), main_server, close()); 346 + } 347 + 348 + async fn close() { 349 + // This function is uh, pruned mostly, because it affected others. 350 + let _ = tokio::signal::ctrl_c().await; 351 + println!("\n\n\nBye!\n"); 352 + process::exit(0); 353 + // let msg = format!("Type [{}] and then [{}] to exit or use '{}' to show more available Lumina server runtime commands.", "q".blue(), "return".bright_magenta(), "help".bright_blue()).bright_yellow(); 354 + // println!("{}", msg); 355 + // let mut input = String::new(); 356 + // let mut waiting = true; 357 + // while waiting { 358 + // input.clear(); 359 + // let _ = std::io::stdout().flush(); 360 + // std::io::stdin() 361 + // .read_line(&mut input) 362 + // .expect("Failed to read input"); 363 + // if input == *"\r\n" { 364 + // waiting = false; 365 + // } 366 + // input = input.replace(['\n', '\r'], ""); 367 + // let split_input = input.as_str().split(' ').collect::<Vec<&str>>(); 368 + // match split_input[0].to_lowercase().as_str() { 369 + // "q" | "x" | "exit" => { 370 + // println!("Bye!"); 371 + // process::exit(0); 372 + // } 373 + // "au" | "adduser" => { 374 + // if split_input.len() < 2 { 375 + // println!("Usage: adduser <username> <password> <email>"); 376 + // } else { 377 + // match database::users::add( 378 + // split_input[1].to_string(), 379 + // split_input[2].to_string(), 380 + // split_input[3].to_string(), 381 + // &config.clone(), 382 + // ) { 383 + // Ok(o) => println!( 384 + // "{}", 385 + // format!( 386 + // "Added user {} with password {} and ID {}.", 387 + // split_input[1].bright_magenta(), 388 + // split_input[2].bright_magenta(), 389 + // o.to_string().bright_magenta(), 390 + // ) 391 + // .green() 392 + // ), 393 + // Err(e) => println!( 394 + // "{}", 395 + // format!( 396 + // "Could not add user {} with password {}: {}", 397 + // split_input[1], 398 + // split_input[2], 399 + // e 400 + // ) 401 + // .red() 402 + // ), 403 + // } 404 + // } 405 + // } 406 + // "h" | "help" => println!( 407 + // "\n{}\n\t{} {}{}{} {}{}{} {}{}{}{}", 408 + // "Lumina server runtime command line - Help\n".bright_yellow(), 409 + // "au | adduser".white(), 410 + // "<".red(), "username".bright_yellow().on_red(), ">".red(), 411 + // "<".red(), "password".bright_yellow().on_red(), ">".red(), 412 + // "<".red(), "email".bright_yellow().on_red(), ">".red(), 413 + // format!("\n\t\tAdds a new user to the database.\n\t{}\n\t\tDisplays this help message.\n\t{}\n\t\tShut down the server.", "h | help".white(), "q | x | exit".white()).green() 414 + // ), 415 + // _ => println!("{}", msg), 416 + // } 417 + // } 418 + } 419 + 420 + mod config; 421 + mod serve; 422 + 423 + #[doc = r"Font file server"] 424 + #[get("/fonts/{a:.*}")] 425 + pub(crate) async fn serve_fonts( 426 + req: HttpRequest, 427 + server_vars_mutex: Data<Mutex<ServerVars>>, 428 + session: Session, 429 + ) -> HttpResponse { 430 + // let reqx = req.clone(); 431 + let fnt: String = req.match_info().get("a").unwrap().parse().unwrap(); 432 + let fonts = fonts(); 433 + let fontbytes: &[u8] = match fnt.as_str() { 434 + "Josefin_Sans/JosefinSans-VariableFont_wght.ttf" => fonts.josefin_sans, 435 + "Fira_Sans/FiraSans-Regular.ttf" => fonts.fira_sans, 436 + "Gantari/Gantari-VariableFont_wght.ttf" => fonts.gantari, 437 + "Syne/Syne-VariableFont_wght.ttf" => fonts.syne, 438 + _ => { 439 + return notfound(server_vars_mutex, req, session).await; 440 + } 441 + }; 442 + let server_vars = ServerVars::grab(&server_vars_mutex).await; 443 + let coninfo = req.connection_info(); 444 + let ip = coninfo.realip_remote_addr().unwrap_or("<unknown IP>"); 445 + server_vars.tell(format!( 446 + "{2}\t{:>45.47}\t\t{}", 447 + format!("/fonts/{}", fnt).magenta(), 448 + ip.yellow(), 449 + "Request/200".bright_green() 450 + )); 451 + HttpResponse::Ok() 452 + .append_header(("Accept-Charset", "UTF-8")) 453 + .content_type("font/ttf") 454 + .body(fontbytes) 455 + } 456 + 457 + #[get("/user/avatar/{a:.*}")] 458 + pub(crate) async fn avatar( 459 + req: HttpRequest, 460 + server_vars_mutex: Data<Mutex<ServerVars>>, 461 + session: Session, 462 + ) -> HttpResponse { 463 + let server_vars = ServerVars::grab(&server_vars_mutex).await; 464 + let user: String = req.match_info().get("a").unwrap().parse().unwrap(); 465 + 466 + // For now unused. Will be used once users can have avatars. 467 + let _ = (user, session); 468 + let coninfo = req.connection_info(); 469 + let ip = coninfo.realip_remote_addr().unwrap_or("<unknown IP>"); 470 + server_vars.tell(format!( 471 + "{2}\t{:>45.47}\t\t{}", 472 + req.path().magenta(), 473 + ip.yellow(), 474 + "Request/200".bright_green() 475 + )); 476 + let index: usize = Rng::gen_range(&mut thread_rng(), 0..=5); 477 + let cont: String = { 478 + let oo = &vec_string_assets_anons_svg()[index]; 479 + 480 + oo.clone().to_string() 481 + }; 482 + HttpResponse::Ok() 483 + .append_header(("Accept-Charset", "UTF-8")) 484 + .content_type("image/svg+xml") 485 + .body(cont) 486 + }
+73
backend-rs/src/tell.rs
··· 1 + /* 2 + * Copyright (c) 2024, MLC 'Strawmelonjuice' Bloeiman 3 + * 4 + * Licensed under the BSD 3-Clause License. See the LICENSE file for more info. 5 + */ 6 + 7 + //! ## Actions for gentle logging ("telling") 8 + //! Logging doesn't need this, but for prettyness these are added as implementations on ServerVars. 9 + 10 + use std::time::SystemTime; 11 + 12 + use colored::Colorize; 13 + use time::{format_description, OffsetDateTime}; 14 + 15 + use crate::{config::LuminaLogConfig, ServerVars}; 16 + 17 + const DATE_FORMAT_STR: &str = "[hour]:[minute]:[second]"; 18 + 19 + #[doc = r"A function that either prints as an [info] log, or prints as [log], depending on configuration. This because loglevel 3 is a bit too verbose, while loglevel 2 is too quiet."] 20 + impl ServerVars { 21 + pub(crate) fn tell(&self, rmsg: impl AsRef<str>) { 22 + let msg = rmsg.as_ref(); 23 + match &self.config.logging.clone() { 24 + None => { 25 + let dt1: OffsetDateTime = SystemTime::now().into(); 26 + let dt_fmt = format_description::parse(DATE_FORMAT_STR).unwrap(); 27 + let times = dt1.format(&dt_fmt).unwrap(); 28 + println!("{} {} {}", times, "[LOG] ".magenta(), msg); 29 + info!("{}", msg); 30 + } 31 + Some(l) => { 32 + l.clone().to_owned().tell(rmsg); 33 + } 34 + } 35 + } 36 + 37 + pub fn format_tell(&self, rmsg: impl AsRef<str>) -> String { 38 + let msg = rmsg.as_ref(); 39 + let dt1: OffsetDateTime = SystemTime::now().into(); 40 + let dt_fmt = format_description::parse(DATE_FORMAT_STR).unwrap(); 41 + let times = dt1.format(&dt_fmt).unwrap(); 42 + format!("{} {} {}", times, "[LOG] ".magenta(), msg) 43 + } 44 + } 45 + impl LuminaLogConfig { 46 + fn tell(self, rmsg: impl AsRef<str>) { 47 + let msg = rmsg.as_ref(); 48 + let a = self; 49 + match a.term_loglevel { 50 + None => { 51 + let dt1: OffsetDateTime = SystemTime::now().into(); 52 + let dt_fmt = format_description::parse(DATE_FORMAT_STR).unwrap(); 53 + let times = dt1.format(&dt_fmt).unwrap(); 54 + println!("{} {} {}", times, "[LOG] ".magenta(), msg); 55 + info!("{}", msg); 56 + } 57 + Some(s) => { 58 + // If the log level is set to erroronly or info-too, just return it as info. The only other case is really just 2, but I am funny. 59 + if s >= 3 || s <= 1 { 60 + info!("{}", msg); 61 + } else { 62 + { 63 + let dt1: OffsetDateTime = SystemTime::now().into(); 64 + let dt_fmt = format_description::parse(DATE_FORMAT_STR).unwrap(); 65 + let times = dt1.format(&dt_fmt).unwrap(); 66 + println!("{} {} {}", times, "[LOG] ".magenta(), msg); 67 + info!("{}", msg); 68 + } 69 + } 70 + } 71 + } 72 + } 73 + }
-13
backend/package.json
··· 1 - { 2 - "$schema": "https://json.schemastore.org/package.json", 3 - "packageManager": "bun@1.1.30+", 4 - "devDependencies": { 5 - "tailwindcss": "latest", 6 - "autoprefixer": "latest", 7 - "postcss": "latest", 8 - "typescript": "^5.6.3", 9 - "postcss-cli": "^11.0.0", 10 - "clean-css-cli": "^5.6.3", 11 - "bun": "latest" 12 - } 13 - }
+5 -1
backend/src/lumina/web/routing.gleam
··· 121 121 ["api", "fe", "fetch-page"] -> api_fe.pagesrverresponder(req, ctx) 122 122 ["api", "fe", "editor_fetch_markdownpreview"] -> 123 123 api_fe.editor_preview_markdown(req, ctx) 124 - _ -> wisp.bad_request() 124 + _ -> 125 + wisp.not_found() 126 + |> wisp.set_body(wisp.Text( 127 + "Invalid POST request" |> string_builder.from_string, 128 + )) 125 129 } 126 130 } 127 131 _ -> {
+44 -23
backend/src/lumina/web/routing/api_fe.gleam
··· 14 14 import gleam/string_builder 15 15 import lumina/data/context.{type Context} 16 16 import lumina/shared/shared_fejsonobject 17 + import lumina/shared/shared_fepage_com.{ 18 + type FEPageServeRequest, type FEPageServeResponse, FEPageServeRequest, 19 + FEPageServeResponse, 20 + } 17 21 import lumina/shared/shared_users 18 22 import lumina/users 19 23 import lumina/web/pages ··· 37 41 None 38 42 } 39 43 } 40 - let username = case uid { 44 + let user = case uid { 41 45 Some(id) -> { 42 46 case users.fetch(ctx, id) { 43 - Some(user) -> user.username 44 - None -> "unset" 47 + Some(user) -> user |> users.to_safe_user 48 + None -> shared_users.SafeUser(id: -1, username: "unset", email: "unset") 45 49 } 46 50 } 47 51 None -> { 48 - "unset" 52 + shared_users.SafeUser(id: -1, username: "unset", email: "unset") 49 53 } 50 54 } 51 55 let clientdata = ··· 55 59 iid: "localhost", 56 60 last_sync: 0, 57 61 ), 58 - user: shared_users.SafeUser(id: -1, username: username, email: "unset"), 62 + user: shared_users.SafeUser( 63 + id: user.id, 64 + username: user.username, 65 + email: user.email, 66 + ), 59 67 ) 60 68 json.object([ 61 69 #( ··· 79 87 } 80 88 81 89 pub fn auth(req: wisp.Request, ctx: context.Context) { 90 + wisp.log_info("Authentication request received.") 82 91 use form <- wisp.require_form(req) 83 92 case form.values { 84 93 [#("password", password), #("username", username)] -> { ··· 86 95 case users.auth(username, password, ctx) { 87 96 Ok(Some(id)) -> { 88 97 // If the user is authenticated, we can store their user ID in the session. 89 - let assert Ok(_) = 90 - wisp_kv_sessions.set( 91 - ctx.session_config, 92 - req, 93 - "uid", 94 - id, 95 - fn(in: Int) { json.int(in) |> json.to_string }, 96 - ) 98 + let handle_session_setting = fn(continue: fn() -> Response) { 99 + case 100 + wisp_kv_sessions.set( 101 + ctx.session_config, 102 + req, 103 + "uid", 104 + id, 105 + fn(in: Int) { json.int(in) |> json.to_string }, 106 + ) 107 + { 108 + Ok(_) -> continue() 109 + Error(e) -> { 110 + wisp.log_critical( 111 + "Error in setting session: " <> string.inspect(e), 112 + ) 113 + wisp.internal_server_error() 114 + } 115 + } 116 + } 117 + 118 + use <- handle_session_setting() 97 119 // Then send them on 98 120 string_builder.from_string("{\"Ok\": true, \"Errorvalue\": \"\"}") 99 121 |> wisp.json_response(200) ··· 101 123 Error(e) -> 102 124 case e { 103 125 users.PasswordIncorrect -> { 104 - string_builder.from_string("{\"Ok\": false}") 126 + string_builder.from_string( 127 + "{\"Ok\": false, \"Errorvalue\": \"No user known with that username-password combination.\"}", 128 + ) 105 129 |> wisp.json_response(401) 106 130 } 107 131 users.InvalidIdentifier -> { ··· 110 134 } 111 135 users.NonexistentUser -> { 112 136 wisp.log_warning("Nonexistent user in auth") 113 - string_builder.from_string("{\"Ok\": false}") 137 + string_builder.from_string( 138 + "{\"Ok\": false, \"Errorvalue\": \"No user known with that username-password combination.\"}", 139 + ) 114 140 |> wisp.json_response(401) 115 141 } 116 142 users.Unspecified -> { ··· 137 163 _ -> { 138 164 wisp.log_warning("Invalid form data in auth") 139 165 wisp.bad_request() 166 + |> wisp.set_body(wisp.Text( 167 + "Invalid form data" |> string_builder.from_string, 168 + )) 140 169 } 141 170 } 142 171 } ··· 197 226 wisp.unprocessable_entity() 198 227 } 199 228 } 200 - } 201 - 202 - type FEPageServeRequest { 203 - FEPageServeRequest(location: String) 204 - } 205 - 206 - type FEPageServeResponse { 207 - FEPageServeResponse(main: String, side: String, message: List(Int)) 208 229 } 209 230 210 231 pub fn pagesrverresponder(req: wisp.Request, ctx: context.Context) {
+3 -9
backend/tailwind.config.js tailwind.config.js
··· 1 - /* 2 - * Copyright (c) 2024, MLC 'Strawmelonjuice' Bloeiman 3 - * 4 - * Licensed under the BSD 3-Clause License. See the LICENSE file for more info. 5 - */ 6 - 7 - /** @type {import('tailwindcss').LuminaConfig} */ 8 1 module.exports = { 9 2 content: [ 10 - "./src-frontend/**/*.{handlebars,ts}", 11 - "./src/lumina/web/**/*.gleam", 3 + "./*frontend/**/*.{handlebars,ts,gleam}", 4 + "./backend/src/lumina/web/**/*.gleam", 5 + "./backend-rs/src/**/*.rs", 12 6 ], 13 7 theme: { 14 8 fontFamily: {
+82 -32
build.sh
··· 1 1 #!/usr/bin/env bash 2 2 3 3 LOCA=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) 4 + GEN_ASSETS="$LOCA/backend/priv/generated" 4 5 SECONDS=0 5 6 QUIET=false 6 7 TESTS=false 7 8 PACK=false 8 9 BUNFLAGS="" 9 10 CARGOFLAGS="" 11 + BCARGOFLAGS="" 10 12 TEST_FE_TS=false 11 13 TEST_FE_GLEAM=false 12 14 # --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 13 15 16 + if [[ "$*" == *"--backend=rust"* ]]; then 17 + GEN_ASSETS="$LOCA/backend-rs/generated" 18 + fi 19 + 14 20 if [[ "$*" == *"--quiet"* ]]; then 15 21 QUIET=true 16 22 fi ··· 20 26 fi 21 27 if [[ "$*" == *"--pack"* ]]; then 22 28 PACK=true 29 + BCARGOFLAGS="--release" 23 30 fi 24 31 if [[ "$*" == *"--run-packed"* ]]; then 25 32 PACK=true 33 + BCARGOFLAGS="--release" 26 34 fi 27 35 if [ "$QUIET" = true ]; then 28 36 echo "[quiet mode]" >&2 ··· 111 119 112 120 # --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 113 121 res_noti 2 "Starting build process..." 114 - rm -rf "$LOCA/backend/priv/generated/js" 115 - mkdir -p "$LOCA/backend/priv/generated/js" 122 + rm -rf "$GEN_ASSETS/js" 123 + mkdir -p "$GEN_ASSETS/js" 124 + bun install $BUNFLAGS 116 125 117 126 if [[ "$*" == *"--frontend=typescript"* ]]; then 118 127 noti "Building front-end (TS)..." ··· 120 129 cd "$LOCA/frontend-ts/" || exit 1 121 130 bun install $BUNFLAGS 122 131 noti "Transpiling and copying to Lumina server..." 123 - bun $BUNFLAGS build "$LOCA/frontend-ts/app.ts" --minify --target=browser --outdir "$LOCA/backend/priv/generated/js/" --sourcemap=linked 124 - bun $BUNFLAGS "$LOCA/tobundle.ts" -- js-1 "$LOCA/backend/priv/generated/js/app.js" 132 + bun $BUNFLAGS build "$LOCA/frontend-ts/app.ts" --minify --target=browser --outdir "$GEN_ASSETS/js/" --sourcemap=linked || { 133 + errnoti "Error while building the frontend." 134 + exit 1 135 + } 136 + bun $BUNFLAGS "$LOCA/tobundle.ts" -- js-1 "$GEN_ASSETS/js/app.js" || { 137 + errnoti "Error while bundling the frontend." 138 + exit 1 139 + } 125 140 else 126 141 if [[ "$*" == *"--frontend=gleam"* ]]; then 127 142 noti "Building front-end (Gleam)..." ··· 136 151 fi 137 152 noti "Copying to Lumina server..." 138 153 echo "import { main } from \"./frontend.mjs\";main();" >"$LOCA/frontend/build/dev/javascript/frontend/app.js" 139 - bun $BUNFLAGS build "$LOCA/frontend/build/dev/javascript/frontend/app.js" --minify --target=browser --outdir "$LOCA/backend/priv/generated/js/" --sourcemap=none 140 - bun $BUNFLAGS "$LOCA/tobundle.ts" -- js-1 "$LOCA/backend/priv/generated/js/app.js" 154 + bun $BUNFLAGS build "$LOCA/frontend/build/dev/javascript/frontend/app.js" --minify --target=browser --outdir "$GEN_ASSETS/js/" --sourcemap=linked || { 155 + errnoti "Error while bundling the frontend." 156 + exit 1 157 + } 158 + bun $BUNFLAGS "$LOCA/tobundle.ts" -- js-1 "$GEN_ASSETS/js/app.js" || { 159 + errnoti "Error while bundling the frontend." 160 + exit 1 161 + } 141 162 else 142 163 errnoti "Invalid or missing frontend option, expected either \"--frontend=typescript\" or \"--frontend=gleam\"." 143 164 if [ "$TESTS" = false ]; then ··· 151 172 fi 152 173 # --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 153 174 noti "Front-end should be done. Continuing to generated assets." 154 - cd "$LOCA/backend/" || exit 1 175 + cd "$LOCA/" || exit 1 155 176 bun install $BUNFLAGS 156 - rm -rf "$LOCA/backend/priv/generated/css/" 157 - mkdir -p "$LOCA/backend/priv/generated/css/" 177 + rm -rf "$GEN_ASSETS/css/" 178 + mkdir -p "$GEN_ASSETS/css/" 158 179 noti "Generating CSS... (TailwindCSS)" 159 - bun x postcss -o "$LOCA/backend/priv/generated/css/main.css" "$LOCA/backend/assets/styles/main.pcss" -u autoprefixer -u tailwindcss 160 - bun "$LOCA/tobundle.ts" -- css-1 "$LOCA/backend/priv/generated/css/main.css" 180 + bun x postcss -o "$GEN_ASSETS/css/main.css" "$LOCA/backend/assets/styles/main.pcss" -u autoprefixer -u tailwindcss 181 + bun "$LOCA/tobundle.ts" -- css-1 "$GEN_ASSETS/css/main.css" 161 182 noti "Minifying CSS and copying to Lumina server..." 162 - bun x cleancss -O1 specialComments:all --inline none "$LOCA/backend/priv/generated/css/main.css" -o "$LOCA/backend/priv/generated/css/main.min.css" 163 - # --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 164 - noti "Compiling Rust libraries..." 165 - cd "$LOCA/rsffi/" || exit 1 166 - if cargo build --release; then 167 - success "\t--> Rust libraries build success." 168 - else 169 - errnoti "\t--> Rust libraries compilation ran into an error." 170 - exit 1 171 - fi 172 - rm -rf "$LOCA/backend/priv/generated/libs/" 173 - mkdir -p "$LOCA/backend/priv/generated/libs/" 174 - noti "Copying Rust libraries to Lumina server..." 175 - cp "$LOCA/rsffi/target/release/librsffi.so" "$LOCA/backend/priv/generated/libs/rsffi.so" 183 + bun x cleancss -O1 specialComments:all --inline none "$GEN_ASSETS/css/main.css" -o "$GEN_ASSETS/css/main.min.css" 176 184 # --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 177 - noti "Starting on Lumina server compilation" 178 - cd "$LOCA/backend/" || exit 1 179 - if gleam build --target erlang; then 180 - success "\t--> Back-end build success." 185 + if [[ "$*" == *"--backend=rust"* ]]; then 186 + noti "Building Rust backend..." 187 + cd "$LOCA/backend-rs/" || exit 1 188 + if cargo build $BCARGOFLAGS; then 189 + success "\t--> Rust backend build success." 190 + else 191 + errnoti "\t--> Rust backend compilation ran into an error." 192 + exit 1 193 + fi 181 194 else 182 - errnoti "\t--> Compilation ran into an error!" 183 - exit 1 195 + if [[ "$*" == *"--backend=gleam"* ]]; then 196 + noti "Compiling Rust libraries..." 197 + cd "$LOCA/rsffi/" || exit 1 198 + if cargo build --release; then 199 + success "\t--> Rust libraries build success." 200 + else 201 + errnoti "\t--> Rust libraries compilation ran into an error." 202 + exit 1 203 + fi 204 + rm -rf "$GEN_ASSETS/libs/" 205 + mkdir -p "$GEN_ASSETS/libs/" 206 + noti "Copying Rust libraries to Lumina server..." 207 + cp "$LOCA/rsffi/target/release/librsffi.so" "$GEN_ASSETS/libs/rsffi.so" 208 + # --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 209 + noti "Starting on Lumina server compilation" 210 + cd "$LOCA/backend/" || exit 1 211 + if gleam build --target erlang; then 212 + success "\t--> Back-end build success." 213 + else 214 + errnoti "\t--> Compilation ran into an error!" 215 + exit 1 216 + fi 217 + else 218 + errnoti "Invalid or missing backend option, expected either \"--backend=gleam\" or \"--backend=rust\"." 219 + if [ "$TESTS" = false ]; then 220 + exit 1 221 + else 222 + noti "This option is not needed for tests, running both backend tests without it." 223 + fi 224 + fi 184 225 fi 185 226 build_duration=$SECONDS 186 227 res_noti 1 "Build completed, took $((build_duration / 60)) minutes and $((build_duration % 60)) seconds." 187 228 if [[ "$*" == *"--run"* ]]; then 188 229 noti "'--run' detected. Running Lumina directly!" 189 - gleam run -- start 230 + if [[ "$*" == *"--backend=rust"* ]]; then 231 + if [ "$PACK" = true ]; then 232 + "$LOCA/backend-rs/target/release/lumina-server" || exit 1 233 + else 234 + "$LOCA/backend-rs/target/debug/lumina-server" || exit 1 235 + fi 236 + else 237 + cd "$LOCA/backend/" || exit 1 238 + gleam run -- start 239 + fi 190 240 else 191 241 if [[ "$*" == *"--pack"* ]]; then 192 242 noti "'--pack' detected. Packaging for deployment."
bun.lockb

This is a binary file and will not be displayed.

+25
frontend/TODO.md
··· 1 + # FE-gleam To-do list 2 + 3 + - [x] Client-server communication 4 + - [x] Periodic updates (FEJSON) 5 + - [x] Fetching FEJSON from server 6 + - [x] Applying auto-actions to FEJSON 7 + - [ ] Verify compile moment thru FEJSON 8 + - [ ] User action reporting 9 + - [ ] New post creation 10 + - [ ] Log out 11 + - [ ] Sub page loading 12 + - [x] Loading sub pages (main, side) 13 + - [ ] Migrating to Handlebars parsing on the client side, instead of server-leveraged strings. 14 + - [ ] Loading special sub pages (editor, settings) 15 + - [ ] Sub page logic 16 + - [ ] Editor 17 + - [ ] Editor UI 18 + - [ ] Editor tab switching 19 + - [ ] Editor Markdown parsing 20 + - [x] Sign in 21 + - [x] Sign in logic 22 + - [x] Sign up 23 + - [x] Sign up logic 24 + - [x] Username check 25 + - [ ] Mobile navigation enhancements
+8 -2
frontend/gleam.toml
··· 1 1 name = "frontend" 2 2 version = "1.0.0" 3 3 target = "javascript" 4 - runtime = "bun" 5 4 # Fill out these fields if you intend to generate HTML documentation or publish 6 5 # your project to the Hex package manager. 7 6 # ··· 20 19 shared = { path = "../shared/" } 21 20 gleam_http = ">= 3.7.0 and < 4.0.0" 22 21 gleam_fetch = ">= 1.0.1 and < 2.0.0" 22 + lustre = ">= 4.5.0 and < 5.0.0" 23 23 gleam_javascript = ">= 0.13.0 and < 1.0.0" 24 - gleam_json = ">= 1.0.1 and < 2.0.0" 24 + gleam_json = ">= 2.0.0 and < 3.0.0" 25 + pprint = ">= 1.0.4 and < 2.0.0" 25 26 26 27 [dev-dependencies] 27 28 gleeunit = ">= 1.0.0 and < 2.0.0" 29 + 30 + [javascript] 31 + sourcemaps = true 32 + typescript_declarations = true 33 + runtime = "bun"
+42
frontend/src/elementactions_ffi.ts
··· 1 + /* 2 + * Copyright (c) 2024, MLC 'Strawmelonjuice' Bloeiman 3 + * Licensed under the BSD 3-Clause License. See the LICENSE file for more info. 4 + * 5 + */ 6 + 7 + /** 8 + * TypeScript FFI module providing DOM manipulation and window utilities for the Gleam frontend. 9 + * This module serves as a bridge between Gleam and browser-specific JavaScript functionality. 10 + */ 11 + 12 + export function disableElement(element: HTMLElement) { 13 + element.setAttribute("disabled", ""); 14 + } 15 + export function elementHidden(element: HTMLElement): boolean { 16 + return element.classList.contains("hidden"); 17 + } 18 + export function enableElement(element: HTMLElement) { 19 + element.removeAttribute("disabled"); 20 + } 21 + export function unHideElement(element: HTMLElement) { 22 + if (element.classList.contains("hidden")) { 23 + element.classList.remove("hidden"); 24 + } 25 + } 26 + export function hideElement(element: HTMLElement) { 27 + if (!element.classList.contains("hidden")) { 28 + element.classList.add("hidden"); 29 + } 30 + } 31 + export function getWindowHost() { 32 + return window.location.host; 33 + } 34 + export function goWindowBack() { 35 + return window.history.back(); 36 + } 37 + export function setWindowLocationHash(to: string) { 38 + return (window.location.hash = to); 39 + } 40 + export function getWindowLocationHash() { 41 + return window.location.hash; 42 + }
+89
frontend/src/fejson_ffi.ts
··· 1 + /* 2 + * Copyright (c) 2024, MLC 'Strawmelonjuice' Bloeiman 3 + * Licensed under the BSD 3-Clause License. See the LICENSE file for more info. 4 + * 5 + */ 6 + 7 + export function setJsonObj(jsonObj: { 8 + instance_iid: string; 9 + instance_lastsync: number; 10 + pulled: number; 11 + user_id: number; 12 + user_username: string; 13 + user_email: string; 14 + }) { 15 + window.fejson = { 16 + instance: { 17 + iid: jsonObj.instance_iid, 18 + lastsync: jsonObj.instance_lastsync, 19 + }, 20 + pulled: jsonObj.pulled, 21 + user: { 22 + id: jsonObj.user_id, 23 + username: jsonObj.user_username, 24 + email: jsonObj.user_email, 25 + }, 26 + }; 27 + } 28 + 29 + export function getJsonObj() { 30 + if (!window.fejson) { 31 + return { 32 + instance_iid: -1, 33 + instance_lastsync: -1, 34 + pulled: 0, 35 + user_id: -1, 36 + user_username: "unset", 37 + user_email: "unset", 38 + }; 39 + } 40 + return { 41 + instance_iid: window.fejson.instance.iid, 42 + instance_lastsync: window.fejson.instance.lastsync, 43 + pulled: window.fejson.pulled, 44 + user_id: window.fejson.user.id, 45 + user_username: window.fejson.user.username, 46 + user_email: window.fejson.user.email, 47 + }; 48 + } 49 + 50 + export function dateToTimestamp(): number { 51 + return Date.now(); 52 + } 53 + 54 + const MAX_QUEUE_SIZE = 1000; 55 + 56 + export function queueFejsonFunction(func: () => null) { 57 + if (!window.fejsonqueue) { 58 + window.fejsonqueue = []; 59 + } 60 + if (window.fejsonqueue.length >= MAX_QUEUE_SIZE) { 61 + console.warn("Function queue size limit reached"); 62 + return; 63 + } 64 + window.fejsonqueue.push(func); 65 + } 66 + 67 + export function getQueuedFejsonFunctions(): Array<() => null> { 68 + return window.fejsonqueue || []; 69 + } 70 + 71 + interface fejsonObject { 72 + pulled: number; 73 + instance: { 74 + iid: string; 75 + lastsync: number; 76 + }; 77 + user: { username: string; id: number; email: string }; 78 + } 79 + declare global { 80 + export interface Window { 81 + mobileMenuToggle: () => null; 82 + on_mobile_swipe_down: Array<(evt: TouchEvent) => null>; 83 + on_mobile_swipe_up: Array<(evt: TouchEvent) => null>; 84 + on_mobile_swipe_right: Array<(evt: TouchEvent) => null>; 85 + on_mobile_swipe_left: Array<(evt: TouchEvent) => null>; 86 + fejsonqueue: Array<() => null>; 87 + fejson: fejsonObject; 88 + } 89 + }
-42
frontend/src/ffi.mjs
··· 1 - // Copyright (c) 2024, MLC 'Strawmelonjuice' Bloeiman 2 - // Licensed under the BSD 3-Clause License. See the LICENSE file for more info. 3 - 4 - export function setJsonObj(jsonObj) { 5 - window.fejson = { 6 - instance: { 7 - iid: jsonObj.instance_iid, 8 - lastsync: jsonObj.instance_lastsync, 9 - }, 10 - pulled: jsonObj.pulled, 11 - user: { 12 - id: jsonObj.user_id, 13 - username: jsonObj.user_username, 14 - email: jsonObj.user_email, 15 - }, 16 - }; 17 - } 18 - 19 - export function getJsonObj() { 20 - if (!window.fejson) { 21 - return { 22 - instance_iid: -1, 23 - instance_lastsync: -1, 24 - pulled: -1, 25 - user_id: -1, 26 - user_username: "unset", 27 - user_email: "unset", 28 - }; 29 - } 30 - return { 31 - instance_iid: window.fejson.instance.iid, 32 - instance_lastsync: window.fejson.instance.lastsync, 33 - pulled: window.fejson.pulled, 34 - user_id: window.fejson.user.id, 35 - user_username: window.fejson.user.username, 36 - user_email: window.fejson.user.email, 37 - }; 38 - } 39 - 40 - export function dateToTimestamp() { 41 - return Number.parseInt(Date.now()); 42 - }
+166 -10
frontend/src/frontend.gleam
··· 1 1 // Copyright (c) 2024, MLC 'Strawmelonjuice' Bloeiman 2 2 // Licensed under the BSD 3-Clause License. See the LICENSE file for more info. 3 3 4 + import frontend/other/element_actions 4 5 import frontend/other/fejson 5 6 import gleam/bool 6 7 import gleam/dynamic ··· 8 9 import gleam/http/request 9 10 import gleam/http/response.{type Response} 10 11 import gleam/int 12 + import gleam/javascript/array.{type Array} 11 13 import gleam/javascript/promise 12 14 import gleam/json 15 + import gleam/list 13 16 import gleam/string 14 - import gleamy_lights/helper as web_io 17 + import gleamy_lights/console 15 18 import gleamy_lights/premixed 16 19 import gleamy_lights/premixed/gleam_colours 17 20 import lumina/shared/shared_fejsonobject 18 21 import lumina/shared/shared_users 22 + import plinth/browser/document 23 + import plinth/browser/element 19 24 import plinth/browser/window 20 25 import plinth/javascript/global 21 26 ··· 30 35 window.add_event_listener("load", fn(_) { 31 36 let path = window.pathname() 32 37 case path { 33 - "/" -> site.index_render() 34 - "/home" -> site.home_render() 35 - "/login" -> login.render() 36 - "/signup" -> signup.render() 37 - _ -> web_io.println("404: Page not found") 38 + "/" | "" -> site.index_render() 39 + "/home" | "/home/" -> site.home_render() 40 + "/login" | "/login/" -> login.render() 41 + "/signup" | "/signup/" -> signup.render() 42 + _ -> console.error("404: Page not found") 38 43 } 39 44 }) 40 - web_io.println( 45 + console.log( 41 46 "Hello from the " 42 47 <> gleam_colours.text_faff_pink("Gleam") 43 48 <> " frontend rewrite!", 44 49 ) 45 50 global.set_interval(4000, update_fejson) 51 + global.set_timeout(0, update_fejson) 52 + // fejson.register_fejson_function(fn() { 53 + // console.log( 54 + // "FEJson instance info: " 55 + // <> premixed.text_lightblue(string.inspect(fejson.get().instance)) 56 + // <> ", last sync: " 57 + // <> premixed.text_lightblue(string.inspect(fejson.get().pulled)) 58 + // <> ".", 59 + // ) 60 + // }) 61 + fejson.register_fejson_function(fn() { 62 + let src = "/user/avatar/" <> int.to_string(fejson.get().user.id) 63 + document.query_selector_all(".ownuseravatarsrc") 64 + |> array.to_list 65 + |> list.each(fn(a) { 66 + use <- 67 + bool.guard( 68 + when: { fejson.get().user.id == -1 }, 69 + return: Nil, 70 + otherwise: _, 71 + ) 72 + 73 + use <- 74 + bool.guard( 75 + when: { a |> element.get_attribute("src") == Ok(src) }, 76 + return: Nil, 77 + otherwise: _, 78 + ) 79 + a 80 + |> element.set_attribute("src", src) 81 + }) 82 + }) 83 + fejson.register_fejson_function(fn() { 84 + let href = "/user/" <> fejson.get().user.username <> "/me" 85 + document.query_selector_all(".ownuserprofilelink") 86 + |> array.to_list 87 + |> list.each(fn(a) { 88 + use <- 89 + bool.guard( 90 + when: { fejson.get().user.id == -1 }, 91 + return: Nil, 92 + otherwise: _, 93 + ) 94 + 95 + use <- 96 + bool.guard( 97 + when: { a |> element.get_attribute("href") == Ok(href) }, 98 + return: Nil, 99 + otherwise: _, 100 + ) 101 + a 102 + |> element.set_attribute("href", href) 103 + }) 104 + }) 105 + 106 + fejson.register_fejson_function(fn() { 107 + let username = fejson.get().user.username 108 + document.query_selector_all(".ownusernametext") 109 + |> array.to_list 110 + |> list.each(fn(a) { 111 + use <- 112 + bool.guard( 113 + when: { fejson.get().user.id == -1 }, 114 + return: Nil, 115 + otherwise: _, 116 + ) 117 + 118 + use <- 119 + bool.guard( 120 + when: { a |> element.inner_text() == username }, 121 + return: Nil, 122 + otherwise: _, 123 + ) 124 + a |> element.set_inner_text(username) 125 + }) 126 + }) 127 + 128 + fejson.register_fejson_function(fn() { 129 + let display_name = fejson.get().user.username 130 + document.query_selector_all(".settodisplayname") 131 + |> array.to_list 132 + |> list.each(fn(a) { 133 + use <- 134 + bool.guard( 135 + when: { fejson.get().user.id == -1 }, 136 + return: Nil, 137 + otherwise: _, 138 + ) 139 + use <- 140 + bool.guard( 141 + when: { a |> element.inner_text() == display_name }, 142 + return: Nil, 143 + otherwise: _, 144 + ) 145 + 146 + console.warn("Display names not implemented yet, using username instead.") 147 + a |> element.set_inner_text(display_name) 148 + }) 149 + }) 150 + global.set_timeout(80, fn() { 151 + global.set_interval(80, fn() { run_fejson_functions() }) 152 + }) 153 + document.add_event_listener("DOMContentLoaded", fn(_) { 154 + mobile_menu_toggle() 155 + let assert Ok(button) = document.get_element_by_id("btn-mobile-menu") 156 + button 157 + |> element.add_event_listener("click", fn(_) { mobile_menu_toggle() }) 158 + Nil 159 + }) 160 + } 161 + 162 + fn mobile_menu_toggle() { 163 + console.info("Toggling mobile menu") 164 + let assert Ok(mobile_menu) = document.get_element_by_id("mobile-menu") 165 + let assert Ok(mobile_menu_button_open) = 166 + document.get_element_by_id("btn-mobile-menu-open") 167 + let assert Ok(mobile_menu_button_close) = 168 + document.get_element_by_id("btn-mobile-menu-close") 169 + case mobile_menu |> element_actions.element_hidden { 170 + True -> { 171 + element_actions.show_element(mobile_menu) 172 + element_actions.hide_element(mobile_menu_button_open) 173 + element_actions.show_element(mobile_menu_button_close) 174 + } 175 + False -> { 176 + element_actions.hide_element(mobile_menu) 177 + element_actions.show_element(mobile_menu_button_open) 178 + element_actions.hide_element(mobile_menu_button_close) 179 + } 180 + } 46 181 } 47 182 48 183 fn update_fejson() { 49 184 let origi = fejson.get() 185 + let first_pull = origi.pulled == 0 50 186 use <- bool.guard( 51 187 when: { 52 188 { fejson.timestamp() - origi.pulled } |> int.absolute_value ··· 71 207 |> promise.await(fn(a: Result(Response(String), FetchError)) { 72 208 case a { 73 209 Ok(b) -> { 74 - // {"instance":{"iid":"localhost","last_sync":0},"user":{"id":-1,"username":"unset"}} 75 210 let now = fn(_) { fejson.timestamp() |> Ok() } 76 211 case 77 212 json.decode( ··· 101 236 { 102 237 Ok(c) -> then(c) 103 238 Error(e) -> 104 - web_io.println( 239 + console.warn( 105 240 premixed.text_lightblue("FEJson fetch ") 106 241 <> premixed.text_error_red(" decoding failed") 107 242 <> ", error:\n" ··· 113 248 } 114 249 } 115 250 Error(e) -> 116 - web_io.println( 251 + console.error( 117 252 premixed.text_lightblue("FEJson fetch ") 118 253 <> premixed.text_error_red(" fetch failed") 119 254 <> ", error:\n" ··· 125 260 } 126 261 use data <- f() 127 262 fejson.set(data) 263 + // run_fejson_functions() 264 + // instead: 265 + case first_pull { 266 + True -> run_fejson_functions() 267 + False -> Nil 268 + } 128 269 } 270 + 271 + /// FE json usually updates every 30 seconds, that means some stuff might change. These functions are ran periodically as well, to keep the frontend in sync with the backend. 272 + /// They'll be fetched from the window object using FFI. Then ran here. 273 + fn run_fejson_functions() { 274 + use <- 275 + bool.guard(when: { fejson.get().pulled == 0 }, return: Nil, otherwise: _) 276 + fetch_fejson_functions() 277 + |> array.to_list 278 + |> list.each(fn(f) { f() }) 279 + 280 + Nil 281 + } 282 + 283 + @external(javascript, "./fejson_ffi.ts", "getQueuedFejsonFunctions") 284 + fn fetch_fejson_functions() -> Array(fn() -> Nil)
+58
frontend/src/frontend/other/element_actions.gleam
··· 1 + // Copyright (c) 2024, MLC 'Strawmelonjuice' Bloeiman 2 + // Licensed under the BSD 3-Clause License. See the LICENSE file for more info. 3 + import gleam/string 4 + import plinth/browser/element.{type Element} 5 + import plinth/browser/window 6 + import gleam/http/request.{type Request} 7 + import gleam/http 8 + 9 + @external(javascript, "../../elementactions_ffi.ts", "disableElement") 10 + pub fn disable_element(a: Element) -> nil 11 + 12 + @external(javascript, "../../elementactions_ffi.ts", "enableElement") 13 + pub fn enable_element(a: Element) -> nil 14 + 15 + @external(javascript, "../../elementactions_ffi.ts", "hideElement") 16 + pub fn hide_element(a: Element) -> nil 17 + 18 + @external(javascript, "../../elementactions_ffi.ts", "unHideElement") 19 + pub fn show_element(a: Element) -> nil 20 + 21 + @external(javascript, "../../elementactions_ffi.ts", "elementHidden") 22 + pub fn element_hidden(a: Element) -> bool 23 + 24 + @external(javascript, "../../elementactions_ffi.ts", "getWindowHost") 25 + pub fn get_window_host() -> String 26 + 27 + @external(javascript, "../../elementactions_ffi.ts", "goWindowBack") 28 + pub fn go_back() -> nil 29 + 30 + @external(javascript, "../../elementactions_ffi.ts", "setWindowLocationHash") 31 + pub fn set_window_location_hash(new: String) -> nil 32 + 33 + @external(javascript, "../../elementactions_ffi.ts", "getWindowLocationHash") 34 + fn int_get_window_location_hash() -> String 35 + 36 + pub fn get_window_location_hash() -> String { 37 + // Remove the leading '#' from the hash if it exists 38 + 39 + let s = int_get_window_location_hash() 40 + case string.starts_with(s, "#") { 41 + True -> s |> string.drop_start(1) 42 + False -> s 43 + } 44 + } 45 + 46 + pub fn phone_home() -> Request(String) { 47 + request.new() 48 + |> request.set_scheme({ 49 + let origin = window.origin() 50 + case origin { 51 + "http://" <> _ -> http.Http 52 + "https://" <> _ -> http.Https 53 + _ -> http.Https 54 + } 55 + }) 56 + 57 + |> request.set_host(get_window_host()) 58 + }
+6 -3
frontend/src/frontend/other/fejson.gleam
··· 45 45 )) 46 46 } 47 47 48 - @external(javascript, "../../ffi.mjs", "getJsonObj") 48 + @external(javascript, "../../fejson_ffi.ts", "getJsonObj") 49 49 fn get_flat() -> FEJsonObjFlat 50 50 51 - @external(javascript, "../../ffi.mjs", "setJsonObj") 51 + @external(javascript, "../../fejson_ffi.ts", "setJsonObj") 52 52 fn set_flat(obj: FEJsonObjFlat) -> nil 53 53 54 - @external(javascript, "../../ffi.mjs", "dateToTimestamp") 54 + @external(javascript, "../../fejson_ffi.ts", "dateToTimestamp") 55 55 pub fn timestamp() -> Int 56 + 57 + @external(javascript, "../../fejson_ffi.ts", "queueFejsonFunction") 58 + pub fn register_fejson_function(a: fn() -> nil) -> nil
+63
frontend/src/frontend/other/formdata.gleam
··· 1 + // Copyright (c) 2024, MLC 'Strawmelonjuice' Bloeiman 2 + // Licensed under the BSD 3-Clause License. See the LICENSE file for more info. 3 + 4 + import gleam/http/request.{type Request} 5 + import gleam/int 6 + import gleam/list 7 + 8 + /// Encode a list of key-value pairs into a multipart form data string. 9 + /// 10 + /// This function replaces the body and content-type header of a request with the encoded form data. 11 + pub fn encode( 12 + req: Request(String), 13 + data: List(#(String, String)), 14 + ) -> Request(String) { 15 + let boundary = 16 + "---------------------------" 17 + <> int.to_string(int.random(1_000_000_000_000_000)) 18 + req 19 + |> request.set_body( 20 + encoder( 21 + data, 22 + // |> dict.to_list 23 + boundary, 24 + "--" <> boundary, 25 + ) 26 + <> "--\r\n", 27 + ) 28 + |> request.prepend_header( 29 + "content-type", 30 + "multipart/form-data; boundary=" <> boundary, 31 + ) 32 + } 33 + 34 + fn encoder( 35 + rest: List(#(String, String)), 36 + boundary: String, 37 + complete: String, 38 + ) -> String { 39 + case list.first(rest) { 40 + Error(_) -> complete 41 + Ok(#(key, value)) -> { 42 + case list.rest(rest) { 43 + Ok(new_rest) -> 44 + encoder( 45 + new_rest, 46 + boundary, 47 + complete 48 + <> "\r\n" 49 + <> "Content-Disposition: form-data; name=\"" 50 + <> key 51 + <> "\"" 52 + <> "\r\n" 53 + <> "\r\n" 54 + <> value 55 + <> "\r\n" 56 + <> "--" 57 + <> boundary, 58 + ) 59 + Error(_) -> complete 60 + } 61 + } 62 + } 63 + }
+174
frontend/src/frontend/other/rendering.gleam
··· 1 + import gleam/bool 2 + import gleam/dynamic/decode 3 + import gleam/json 4 + import gleam/list 5 + import gleam/result 6 + import lumina/shared/shared_fepage_com.{ 7 + type FEPageServeResponse, FEPageServeResponse, 8 + } 9 + import lustre/attribute.{attribute} 10 + import lustre/element.{text} 11 + import lustre/element/html 12 + import lustre/element/svg 13 + 14 + type HomePageData { 15 + HomePageData(username: String, instance_name: String) 16 + } 17 + 18 + fn home_page_data_decoder() -> decode.Decoder(HomePageData) { 19 + use username <- decode.field("username", decode.string) 20 + use instance_name <- decode.field("instance_name", decode.string) 21 + decode.success(HomePageData(username:, instance_name:)) 22 + } 23 + 24 + type NotificationsPageData { 25 + NotificationsPageData(notifs: List(Notification), unread_count: Int) 26 + } 27 + 28 + fn notifications_page_data_decoder() -> decode.Decoder(NotificationsPageData) { 29 + use notifs <- decode.field("notifs", decode.list(notification_decoder())) 30 + use unread_count <- decode.field("unread_count", decode.int) 31 + decode.success(NotificationsPageData(notifs:, unread_count:)) 32 + } 33 + 34 + type Notification { 35 + Notification( 36 + title: String, 37 + content: String, 38 + time: String, 39 + recently_read: Bool, 40 + kind: String, 41 + ) 42 + } 43 + 44 + fn notification_decoder() -> decode.Decoder(Notification) { 45 + use title <- decode.field("title", decode.string) 46 + use content <- decode.field("content", decode.string) 47 + use time <- decode.field("time", decode.string) 48 + use recently_read <- decode.field("recently_read", decode.bool) 49 + use kind <- decode.field("kind", decode.string) 50 + decode.success(Notification(title:, content:, time:, recently_read:, kind:)) 51 + } 52 + 53 + /// This module handles rendering of sub-pages, doing this through checking if a code 899 is present in the messages, and if so. 54 + /// Rendering the data from the `FEPageServeResponse` into, for example, Lustre templates. 55 + /// If the code 899 is not present, the HTML is already rendered into the data fields. Render will just return. 56 + pub fn renders(source: FEPageServeResponse) -> FEPageServeResponse { 57 + // If the message does not contain the code 899, that means the HTML is already rendered into the data fields. 58 + use <- bool.guard(list.contains(source.message, 899) |> bool.negate, source) 59 + // Okay, 899 means we have to decode the data before rendering it. 60 + rendermatcher(source, source) 61 + } 62 + 63 + fn rendermatcher( 64 + source: FEPageServeResponse, 65 + osource: FEPageServeResponse, 66 + ) -> FEPageServeResponse { 67 + case source.message { 68 + // If the message is empty, we just return the source. This might need to change to display an error message some point in the future, as it might be a sign of a bug or at least discrepancy between the client and server. 69 + [] -> osource 70 + [901, ..] -> { 71 + // Home page/timeline page! 72 + // We have to decode the data before rendering it. 73 + let data = 74 + json.parse(from: source.main, using: home_page_data_decoder()) 75 + |> result.unwrap(HomePageData("", "&gt;error&lt;")) 76 + let right = 77 + html.div([], [ 78 + html.h1([], [ 79 + html.text("welcome to instance "), 80 + html.code([], [html.text(data.instance_name)]), 81 + ]), 82 + html.p([], [ 83 + html.text( 84 + "as you can see, there is no such thing as a homepage. lumina is 85 + not ready for anything yet. 86 + ", 87 + ), 88 + ]), 89 + ]) 90 + |> element.to_string() 91 + let left = 92 + html.div([attribute.class("lg:p-0.5")], [ 93 + html.div( 94 + [ 95 + attribute.class( 96 + "flex flex-col p-4 m-4 space-y-4 border-gray-500 rounded-md border-1 bg-brown-100 dark:bg-neutral-700", 97 + ), 98 + ], 99 + [ 100 + html.h3([attribute.class("underline")], [html.text("Timelines")]), 101 + html.a( 102 + [attribute("onclick", ""), attribute.href("javascript:void(0)")], 103 + [ 104 + html.button( 105 + [ 106 + attribute.class( 107 + "px-3 py-2 text-sm font-medium bg-orange-200 border-2 rounded-md border-emerald-600 dark:text-orange-200 dark:bg-yellow-700 hover:text-white hover:bg-gray-700 text-brown-800 dark:border-zinc-400", 108 + ), 109 + ], 110 + [html.text("🌐 Global timeline")], 111 + ), 112 + ], 113 + ), 114 + html.a( 115 + [attribute("onclick", ""), attribute.href("javascript:void(0)")], 116 + [ 117 + html.button( 118 + [ 119 + attribute.class( 120 + "px-3 py-2 text-sm font-medium bg-orange-200 border-2 rounded-md border-emerald-600 dark:text-orange-200 dark:bg-yellow-700 hover:text-white hover:bg-gray-700 text-brown-800 dark:border-zinc-400", 121 + ), 122 + ], 123 + [html.text("🙋 Followed timeline")], 124 + ), 125 + ], 126 + ), 127 + ], 128 + ), 129 + html.div( 130 + [ 131 + attribute.class( 132 + "flex flex-col p-4 m-4 space-y-4 border-gray-500 rounded-md border-1 bg-brown-100 dark:bg-neutral-700", 133 + ), 134 + ], 135 + [html.h3([attribute.class("underline")], [html.text("Bubbles")])], 136 + ), 137 + ]) 138 + |> element.to_string() 139 + FEPageServeResponse(right, left, osource.message) 140 + } 141 + [909, ..] -> { 142 + // Notifications page! 143 + // We have to decode the data before rendering it. 144 + let data = 145 + json.parse(from: source.main, using: notifications_page_data_decoder()) 146 + |> result.unwrap(NotificationsPageData([], 0)) 147 + 148 + let right = 149 + html.html([attribute("lang", "en")], [ 150 + html.head([], [ 151 + html.meta([attribute("charset", "UTF-8")]), 152 + html.title([], "Notifications"), 153 + html.meta([ 154 + attribute("content", "width=device-width, initial-scale=1.0"), 155 + attribute.name("viewport"), 156 + ]), 157 + ]), 158 + html.body([], []), 159 + ]) 160 + |> element.to_string() 161 + let left = "" 162 + 163 + FEPageServeResponse(right, left, osource.message) 164 + } 165 + 166 + [_, ..rest] -> { 167 + // More luck next loop! 168 + rendermatcher( 169 + { FEPageServeResponse(source.main, source.side, rest) }, 170 + osource, 171 + ) 172 + } 173 + } 174 + }
+215 -7
frontend/src/frontend/page/login.gleam
··· 1 1 // Copyright (c) 2024, MLC 'Strawmelonjuice' Bloeiman 2 2 // Licensed under the BSD 3-Clause License. See the LICENSE file for more info. 3 3 4 - import gleamy_lights/helper as web_io 4 + import frontend/other/element_actions 5 + import frontend/other/funnyrandomusername.{funnyrandomusername} 6 + import gleam/dynamic.{field} 7 + import gleam/fetch 8 + import gleam/http.{Post} 9 + import gleam/http/request 10 + import gleam/javascript/array 11 + import gleam/javascript/promise 12 + import gleam/json 13 + import gleam/list 14 + import gleam/result 15 + import gleam/string 16 + import gleamy_lights/console 5 17 import gleamy_lights/premixed 6 18 import plinth/browser/document 7 19 import plinth/browser/element 20 + import plinth/browser/window 21 + import plinth/javascript/global 22 + import plinth/javascript/storage 23 + import pprint 8 24 9 25 // import plinth/browser/event.{type Event} 10 26 11 27 pub fn render() { 12 - web_io.println( 28 + console.log( 13 29 "Detected you are on the " <> premixed.text_lime("login page") <> ".", 14 30 ) 15 31 let assert Ok(submitbutton) = document.get_element_by_id("submitbutton") 16 - element.add_event_listener(submitbutton, "click", try_login) 17 - // Just to show we now can use the element. 18 - element.set_attribute(submitbutton, "data-identified-as", "Login") 32 + element.add_event_listener(submitbutton, "click", fn(_) { 33 + try_login(submitbutton) 34 + Nil 35 + }) 36 + let assert Ok(local_storage) = storage.local() 37 + case storage.get_item(local_storage, "AutologinUsername") { 38 + Ok(username) -> { 39 + case storage.get_item(local_storage, "AutologinPassword") { 40 + Ok(password) -> { 41 + let assert Ok(d) = document.get_element_by_id("Aaa1") 42 + d 43 + |> element.set_inner_text("Logging in automatically...") 44 + authentication_request(username, password, True) 45 + Nil 46 + } 47 + _ -> Nil 48 + } 49 + } 50 + _ -> Nil 51 + } 52 + document.query_selector_all("#username") 53 + |> array.to_list() 54 + |> list.each(fn(e) { 55 + e |> element.set_attribute("placeholder", funnyrandomusername()) 56 + }) 57 + Nil 58 + } 59 + 60 + fn try_login(submitbutton: element.Element) { 61 + console.log("Trying authentication...") 62 + submitbutton 63 + |> element.set_inner_html( 64 + "<div style=\"background-image: url('/spinner.svg'); background-repeat: no-repeat; background-size: cover;\" class=\"relative w-10 h-10 pl-max pr-max\"></div>", 65 + ) 66 + submitbutton |> element_actions.disable_element 67 + global.set_timeout(9600, fn() { 68 + submitbutton |> element.set_inner_text("Retry") 69 + submitbutton |> element_actions.enable_element 70 + }) 71 + { 72 + let assert Ok(d) = document.get_element_by_id("Aaa1") 73 + d 74 + } 75 + |> element.set_inner_text("Checking credentials...") 76 + 77 + // timeout to allow spinner to show up 78 + global.set_timeout(500, fn() { 79 + let username = { 80 + let assert Ok(d) = document.get_element_by_id("username") 81 + let assert Ok(v) = d |> element.value 82 + v 83 + } 84 + let password = { 85 + let assert Ok(d) = document.get_element_by_id("password") 86 + let assert Ok(v) = d |> element.value 87 + v 88 + } 89 + authentication_request(username, password, False) 90 + }) 19 91 } 20 92 21 - fn try_login(_a) { 22 - web_io.println("Trying to login...") 93 + fn authentication_request( 94 + username: String, 95 + password: String, 96 + is_autologin: Bool, 97 + ) { 98 + let req = 99 + element_actions.phone_home() 100 + |> request.set_method(Post) 101 + |> request.set_path("/api/fe/auth/") 102 + |> request.set_body( 103 + json.object([ 104 + #("username", json.string(username)), 105 + #("password", json.string(password)), 106 + ]) 107 + |> json.to_string, 108 + ) 109 + |> request.set_header("Content-Type", "application/json") 110 + 111 + // |> formdata.encode([#("username", username), #("password", password)]) 112 + // |> request.prepend_header("accept", "application/vnd.hmrc.1.0+json") 113 + 114 + fetch.send(req) 115 + |> promise.try_await(fetch.read_json_body) 116 + |> promise.await(fn(resp) { 117 + let assert Ok(resp) = resp 118 + // We don't care about the status code, we just want to know if the request was successful. 119 + // let assert 200 = resp.status 120 + // let assert Ok("application/json; charset=utf-8") = 121 + // response.get_header(resp, "content-type") 122 + case 123 + resp.body 124 + |> dynamic.decode2( 125 + AuthResponse, 126 + field("Ok", of: dynamic.bool), 127 + field("Errorvalue", of: dynamic.string), 128 + ) 129 + { 130 + Error(e) -> { 131 + console.error( 132 + "Error decoding server auth response: " 133 + <> string.inspect(e) 134 + <> "\n\nGot: " 135 + <> pprint.format(resp.body), 136 + ) 137 + panic 138 + } 139 + Ok(authorisation_response) -> { 140 + continue_after_login(authorisation_response, is_autologin) 141 + } 142 + } 143 + promise.resolve(Ok(Nil)) 144 + }) 145 + } 146 + 147 + /// This function is called after the user has send a login request. It checks if the login was successful and continues the user to the next page. 148 + /// If the login was not successful, it will show an error message. 149 + /// If the user checked the "Remember me" checkbox, it will save the login data in the local storage. 150 + fn continue_after_login( 151 + authorisation_response: AuthResponse, 152 + is_autologin: Bool, 153 + ) { 154 + console.log("Login answer was received, let's unpack it.") 155 + case authorisation_response { 156 + AuthResponse(True, _) -> { 157 + console.info("Spoiler alert: Login is succesful.") 158 + let timeout = case is_autologin { 159 + True -> { 160 + 0 161 + } 162 + False -> { 163 + let assert Ok(d) = document.get_element_by_id("Aaa1") 164 + d 165 + |> element.set_inner_text( 166 + "Login successful, you will be forwarded now.", 167 + ) 168 + 2000 169 + } 170 + } 171 + 172 + case 173 + { 174 + let assert Ok(autologincheckbox) = 175 + document.get_element_by_id("autologin") 176 + autologincheckbox |> element.get_checked 177 + } 178 + { 179 + True -> { 180 + let username = { 181 + let assert Ok(d) = document.get_element_by_id("username") 182 + let assert Ok(v) = d |> element.value 183 + v 184 + } 185 + let password = { 186 + let assert Ok(d) = document.get_element_by_id("password") 187 + let assert Ok(v) = d |> element.value 188 + v 189 + } 190 + let assert Ok(storage) = storage.local() 191 + let assert Ok(_) = 192 + [ 193 + storage.set_item(storage, "AutologinUsername", username), 194 + storage.set_item(storage, "AutologinPassword", password), 195 + ] 196 + |> result.all() 197 + Nil 198 + } 199 + False -> { 200 + Nil 201 + } 202 + } 203 + global.set_timeout(timeout, fn() { 204 + window.set_location( 205 + window.self(), 206 + "/home/" 207 + <> { 208 + case window.get_hash() { 209 + // const loginPageList = ["home", "notifications", "test"]; 210 + Ok("home") -> "#home" 211 + Ok("notifications") -> "#notifications" 212 + Ok("test") -> "#test" 213 + _ -> "" 214 + } 215 + }, 216 + ) 217 + }) 218 + } 219 + AuthResponse(False, error) -> { 220 + let assert Ok(d) = document.get_element_by_id("Aaa1") 221 + d |> element.set_inner_text("Login failed: " <> error) 222 + let assert Ok(submitbutton) = document.get_element_by_id("submitbutton") 223 + submitbutton |> element.set_inner_text("Retry") 224 + submitbutton |> element_actions.enable_element 225 + } 226 + } 227 + } 228 + 229 + type AuthResponse { 230 + AuthResponse(ok: Bool, error_value: String) 23 231 }
+240 -3
frontend/src/frontend/page/signup.gleam
··· 1 1 // Copyright (c) 2024, MLC 'Strawmelonjuice' Bloeiman 2 2 // Licensed under the BSD 3-Clause License. See the LICENSE file for more info. 3 3 4 - import gleamy_lights/helper as web_io 4 + import frontend/other/element_actions 5 + import gleam/dynamic.{field} 6 + import gleam/dynamic/decode 7 + import gleam/fetch 8 + import gleam/http.{Post} 9 + import gleam/http/request 10 + import gleam/javascript/promise 11 + import gleam/json 12 + import gleam/option.{type Option, None, Some} 13 + import gleam/string 14 + import gleamy_lights/console 15 + import gleamy_lights/premixed 16 + import plinth/browser/document 17 + import plinth/browser/element 18 + import plinth/browser/window 19 + import plinth/javascript/global 20 + 21 + pub fn render() -> Nil { 22 + console.log( 23 + "Detected you are on the " <> premixed.text_lilac("signup page") <> ".", 24 + ) 25 + let assert Ok(usernamebox) = document.get_element_by_id("username") 26 + usernamebox 27 + |> element.add_event_listener("change", fn(_) { checkusername(usernamebox) }) 28 + 29 + let assert Ok(passwordbox) = document.get_element_by_id("password") 30 + passwordbox 31 + |> element.add_event_listener("change", fn(_) { checkpassword(passwordbox) }) 32 + let assert Ok(submitbutton) = document.get_element_by_id("submitbutton") 33 + element.add_event_listener(submitbutton, "click", fn(_) { 34 + try_signup(submitbutton) 35 + Nil 36 + }) 37 + 38 + Nil 39 + } 40 + 41 + fn checkusername(usernamebox) -> Nil { 42 + let assert Ok(entered_username) = usernamebox |> element.value() 43 + console.log( 44 + "Checking if the username " <> entered_username <> " is available...", 45 + ) 46 + element_actions.phone_home() 47 + |> request.set_method(Post) 48 + |> request.set_path("/api/fe/auth-create/check-username") 49 + |> request.set_body( 50 + json.object([#("u", json.string(entered_username))]) 51 + |> json.to_string, 52 + ) 53 + |> request.set_header("Content-Type", "application/json") 54 + |> fetch.send() 55 + |> promise.try_await(fetch.read_json_body) 56 + |> promise.await(fn(resp) { 57 + let assert Ok(resp) = resp 58 + let assert 200 = resp.status 59 + let check_response = case 60 + resp.body 61 + |> decode.run(username_check_response_decoder()) 62 + { 63 + Ok(v) -> v 64 + Error(e) -> { 65 + console.error( 66 + { 67 + "Error decoding the response: " 68 + <> string.inspect(e) |> premixed.text_error_red 69 + } 70 + <> "\n\n" 71 + <> string.inspect(resp.body), 72 + ) 73 + UsernameCheckResponse(False, None) 74 + } 75 + } 76 + case check_response { 77 + UsernameCheckResponse(True, _) -> { 78 + let assert Ok(user_name_label) = 79 + document.get_element_by_id("usernameLabel") 80 + element.set_inner_html( 81 + user_name_label, 82 + "Username&emsp;&emsp;<span class=\"text-green-300\">⬅ Username is available!&nbsp;<img src=\"/green-check.svg\" class=\"inline\"></span>", 83 + ) 84 + } 85 + UsernameCheckResponse(False, Some(why)) -> { 86 + let assert Ok(user_name_label) = 87 + document.get_element_by_id("usernameLabel") 88 + element.set_inner_html(user_name_label, { 89 + case why { 90 + "InvalidChars" -> 91 + "Username&emsp;&emsp;<span class=\"text-red-300\">⬅ There are characters in this username that are not allowed!&nbsp;<img src=\"/red-cross.svg\" class=\"inline\"></span>" 92 + "TooShort" -> 93 + "Username&emsp;&emsp;<span class=\"text-red-300\">⬅ That username is too short!&nbsp;<img src=\"/red-cross.svg\" class=\"inline\"></span>" 94 + "userExists" -> 95 + "Username&emsp;&emsp;<span class=\"text-red-300\">⬅ Someone already claimed that username!&nbsp;<img src=\"/red-cross.svg\" class=\"inline\" alt=\"X\"></span>" 96 + _ -> 97 + "Username&emsp;&emsp;<span class=\"text-red-300\">⬅ Username is not available!&nbsp;<img src=\"/red-cross.svg\" class=\"inline\" alt=\"X\"></span>" 98 + } 99 + }) 100 + } 101 + _ -> { 102 + Nil 103 + } 104 + } 105 + promise.resolve(Ok(Nil)) 106 + }) 107 + Nil 108 + } 109 + 110 + type UsernameCheckResponse { 111 + UsernameCheckResponse(ok: Bool, why: Option(String)) 112 + } 113 + 114 + fn username_check_response_decoder() -> decode.Decoder(UsernameCheckResponse) { 115 + use ok <- decode.field("Ok", decode.bool) 116 + use why <- decode.optional_field("Why", None, decode.optional(decode.string)) 117 + decode.success(UsernameCheckResponse(ok:, why:)) 118 + } 119 + 120 + fn checkpassword(passwordbox) -> Nil { 121 + let assert Ok(entered_username) = passwordbox |> element.value() 122 + // This is not yet implemented in the backend 123 + console.log( 124 + "Checking if the password " <> entered_username <> " is available...", 125 + ) 126 + console.log(premixed.text_error_red( 127 + "Password check feature is not yet implemented. Also see <https://github.com/strawmelonjuice/lumina/issues/48> for this.", 128 + )) 129 + Nil 130 + } 131 + 132 + fn try_signup(submitbutton: element.Element) { 133 + console.log("Trying registration...") 134 + submitbutton 135 + |> element.set_inner_html( 136 + "<div style=\"background-image: url('/spinner.svg'); background-repeat: no-repeat; background-size: cover;\" class=\"relative w-10 h-10 pl-max pr-max\"></div>", 137 + ) 138 + submitbutton |> element_actions.disable_element 139 + global.set_timeout(9600, fn() { 140 + submitbutton |> element.set_inner_text("Retry") 141 + submitbutton |> element_actions.enable_element 142 + }) 143 + { 144 + let assert Ok(d) = document.get_element_by_id("Aaa1") 145 + d 146 + } 147 + |> element.set_inner_text("Checking credentials...") 148 + 149 + // timeout to allow spinner to show up 150 + global.set_timeout(500, fn() { 151 + let username = { 152 + let assert Ok(d) = document.get_element_by_id("username") 153 + let assert Ok(v) = d |> element.value 154 + v 155 + } 156 + let password = { 157 + let assert Ok(d) = document.get_element_by_id("password") 158 + let assert Ok(v) = d |> element.value 159 + v 160 + } 161 + let email = { 162 + let assert Ok(d) = document.get_element_by_id("email") 163 + let assert Ok(v) = d |> element.value 164 + v 165 + } 166 + registration_request(username, email, password) 167 + }) 168 + } 169 + 170 + fn registration_request(username: String, email: String, password: String) { 171 + let req = 172 + element_actions.phone_home() 173 + |> request.set_method(Post) 174 + |> request.set_path("/api/fe/auth-create/") 175 + |> request.set_body( 176 + json.object([ 177 + #("username", json.string(username)), 178 + #("password", json.string(password)), 179 + #("email", json.string(email)), 180 + ]) 181 + |> json.to_string, 182 + ) 183 + |> request.set_header("Content-Type", "application/json") 184 + // |> request.prepend_header("accept", "application/vnd.hmrc.1.0+json") 185 + 186 + fetch.send(req) 187 + |> promise.try_await(fetch.read_json_body) 188 + |> promise.await(fn(resp) { 189 + let assert Ok(resp) = resp 190 + // We don't care about the status code, we just want to know if the request was successful. 191 + // let assert 200 = resp.status 192 + // let assert Ok("application/json; charset=utf-8") = response.get_header(resp, "content-type") 193 + let assert Ok(registration_response) = 194 + resp.body 195 + |> dynamic.decode2( 196 + RegistrationResponse, 197 + field("Ok", of: dynamic.bool), 198 + field("Errorvalue", of: dynamic.string), 199 + ) 200 + registration_response |> continue_after_signup() 201 + promise.resolve(Ok(Nil)) 202 + }) 203 + } 5 204 6 - pub fn render() { 7 - web_io.println("Login page") 205 + type RegistrationResponse { 206 + RegistrationResponse(ok: Bool, error_value: String) 207 + } 208 + 209 + fn continue_after_signup(registration_response) { 210 + case registration_response { 211 + RegistrationResponse(True, _) -> { 212 + let assert Ok(d) = document.get_element_by_id("Aaa1") 213 + d 214 + |> element.set_inner_text( 215 + "Sign-up successful! You will be forwarded now.", 216 + ) 217 + 218 + global.set_timeout(3000, fn() { 219 + window.set_location( 220 + window.self(), 221 + "/home/" 222 + <> { 223 + case window.get_hash() { 224 + // const loginPageList = ["home", "notifications", "test"]; 225 + Ok("home") -> "#home" 226 + Ok("notifications") -> "#notifications" 227 + Ok("test") -> "#test" 228 + _ -> "" 229 + } 230 + }, 231 + ) 232 + }) 233 + } 234 + RegistrationResponse(False, _) -> { 235 + let assert Ok(d) = document.get_element_by_id("Aaa1") 236 + d 237 + |> element.set_inner_text( 238 + "<div style=\"background-image: url('/red-cross.svg'); background-repeat: no-repeat; background-size: cover;\" class=\"relative w-10 h-10 pl-max pr-max\"></div>", 239 + ) 240 + let assert Ok(submitbutton) = document.get_element_by_id("submitbutton") 241 + submitbutton |> element.set_inner_text("Sign up") 242 + submitbutton |> element_actions.enable_element 243 + } 244 + } 8 245 }
+477 -7
frontend/src/frontend/page/site.gleam
··· 1 1 // Copyright (c) 2024, MLC 'Strawmelonjuice' Bloeiman 2 2 // Licensed under the BSD 3-Clause License. See the LICENSE file for more info. 3 3 4 - import gleamy_lights/helper as web_io 4 + import frontend/other/rendering 5 + import gleam/bool 6 + import gleam/javascript/array 7 + import gleam/list 8 + import lumina/shared/shared_fepage_com.{ 9 + type FEPageServeResponse, FEPageServeResponse, 10 + } 11 + import plinth/browser/event 12 + import plinth/javascript/global 13 + import plinth/javascript/storage 14 + 15 + import frontend/other/element_actions 16 + import gleam/dict.{type Dict} 17 + import gleam/dynamic 18 + import gleam/fetch 19 + import gleam/http 20 + import gleam/http/request 21 + import gleam/http/response 22 + import gleam/javascript/promise 23 + import gleam/json 24 + import gleam/string 25 + import gleamy_lights/console 5 26 import gleamy_lights/premixed 6 27 import gleamy_lights/premixed/gleam_colours 28 + import plinth/browser/document 29 + import plinth/browser/element 30 + import plinth/browser/window 7 31 8 32 pub fn home_render() { 9 - web_io.println( 10 - "Detected you are on the " <> premixed.text_pink("home page") <> ".", 11 - ) 33 + console.log( 34 + "Detected you are on the " <> premixed.text_pink("home page") <> ".", 35 + ) 36 + let sub_page_list: SubPageList = 37 + dict.new() 38 + |> dict.insert( 39 + "home", 40 + SubPageMeta( 41 + { 42 + let assert Ok(a) = document.query_selector("#home-nav") 43 + a 44 + }, 45 + { 46 + let assert Ok(a) = document.query_selector("#mobile-home-nav") 47 + a 48 + }, 49 + fn() { 50 + let assert Ok(a) = document.query_selector("#mobiletimelineswitcher") 51 + a |> element_actions.show_element() 52 + Nil 53 + }, 54 + "home", 55 + True, 56 + ), 57 + ) 58 + |> dict.insert( 59 + "test", 60 + SubPageMeta( 61 + { 62 + let assert Ok(a) = document.query_selector("#test-nav") 63 + a 64 + }, 65 + { 66 + let assert Ok(a) = document.query_selector("#mobile-test-nav") 67 + a 68 + }, 69 + fn() { 70 + let assert Ok(a) = document.query_selector("#mobiletimelineswitcher") 71 + a |> element_actions.hide_element() 72 + Nil 73 + }, 74 + "test", 75 + True, 76 + ), 77 + ) 78 + |> dict.insert( 79 + "editor", 80 + SubPageMeta( 81 + { 82 + let assert Ok(a) = document.query_selector("#home-nav") 83 + a 84 + }, 85 + { 86 + let assert Ok(a) = document.query_selector("#mobile-home-nav") 87 + a 88 + }, 89 + fn() { editor_unfold() }, 90 + "editor", 91 + False, 92 + ), 93 + ) 94 + |> dict.insert( 95 + "notifications", 96 + SubPageMeta( 97 + { 98 + let assert Ok(a) = document.query_selector("#notifications-nav") 99 + a 100 + }, 101 + { 102 + let assert Ok(a) = 103 + document.query_selector("#mobile-notifications-nav") 104 + a 105 + }, 106 + fn() { 107 + let assert Ok(a) = document.query_selector("#mobiletimelineswitcher") 108 + a |> element_actions.hide_element() 109 + Nil 110 + }, 111 + "notifications", 112 + True, 113 + ), 114 + ) 115 + console.log( 116 + "Subpage list: " <> premixed.text_lightblue(string.inspect(sub_page_list)), 117 + ) 118 + global.set_interval(60, fn() { 119 + check_if_page_needs_to_be_switched(sub_page_list) 120 + }) 121 + editor_fold() 122 + { 123 + let assert Ok(a) = 124 + document.get_element_by_id("switchpageNotificationsTrigger") 125 + a 126 + |> element.add_event_listener("click", fn(_) { 127 + switch_subpage("notifications", "special click event", sub_page_list) 128 + Nil 129 + }) 130 + } 131 + { 132 + let assert Ok(a) = document.get_element_by_id("editorTrigger") 133 + a 134 + |> element.add_event_listener("click", fn(_) { 135 + trigger_editor() 136 + Nil 137 + }) 138 + } 139 + { 140 + let a = document.query_selector_all(".logoutbutton") 141 + a 142 + |> array.to_list 143 + |> list.each(fn(b) { 144 + b 145 + |> element.add_event_listener("click", fn(_) { 146 + let assert Ok(stor) = storage.local() 147 + storage.clear(stor) 148 + window.set_location(window.self(), "/session/logout") 149 + Nil 150 + }) 151 + }) 152 + } 153 + { 154 + document.add_event_listener("keydown", fn(event) { 155 + case event |> event.key() |> string.lowercase() { 156 + "e" -> { 157 + event |> event.prevent_default() 158 + trigger_editor() 159 + } 160 + "h" -> { 161 + event |> event.prevent_default() 162 + window.set_location(window.self(), "#home") 163 + } 164 + "n" -> { 165 + event |> event.prevent_default() 166 + window.set_location(window.self(), "#notifications") 167 + } 168 + _ -> Nil 169 + } 170 + }) 171 + } 172 + Nil 173 + } 174 + 175 + fn editor_unfold() { 176 + let assert Ok(mobiletimelineswitcher) = 177 + document.query_selector("#mobiletimelineswitcher") 178 + let assert Ok(posteditor) = document.query_selector("div#posteditor") 179 + let errormsg = 180 + "<p class=\"w-full h-full text-black bg-white dark:text-white dark:bg-black\">Failed to load post editor.</p>" 181 + mobiletimelineswitcher |> element_actions.hide_element() 182 + posteditor |> element_actions.show_element() 183 + case document.body() |> element.dataset_get("editorOpen") { 184 + Ok("initial") -> { 185 + fetch_editor() 186 + Nil 187 + } 188 + _ -> Nil 189 + } 190 + 191 + todo 192 + } 193 + 194 + fn trigger_editor() { 195 + let hash = element_actions.get_window_location_hash() 196 + 197 + case document.body() |> element.dataset_get("editorOpen") { 198 + Ok("true") -> { 199 + console.info( 200 + "triggerEditor: got called, but editor is already open. Refolding editor instead.", 201 + ) 202 + editor_fold() 203 + } 204 + _ -> { 205 + case hash == "editor" { 206 + True -> { 207 + // Editor glitched out, going back to retry... 208 + console.log("triggerEditor: retrying...") 209 + element_actions.go_back() 210 + global.set_timeout(600, fn() { 211 + element_actions.set_window_location_hash("editor") 212 + Nil 213 + }) 214 + Nil 215 + } 216 + False -> { 217 + element_actions.set_window_location_hash("editor") 218 + } 219 + } 220 + } 221 + } 222 + } 223 + 224 + fn editor_fold() { 225 + let assert Ok(posteditor) = document.query_selector("div#posteditor") 226 + posteditor |> element_actions.hide_element() 227 + case document.body() |> element.dataset_get("editorOpen") { 228 + Ok(_) -> 229 + document.body() |> element.set_attribute("data-editor-open", "false") 230 + Error(_) -> 231 + document.body() |> element.set_attribute("data-editor-open", "initial") 232 + } 233 + } 234 + 235 + fn fetch_page(page: String, then: fn(Result(FEPageServeResponse, Nil)) -> Nil) { 236 + { 237 + let req = 238 + { 239 + let assert Ok(a) = request.to(window.origin() <> "/api/fe/fetch-page") 240 + a 241 + } 242 + |> request.set_body("{\"location\": \"" <> page <> "\"}") 243 + |> request.set_header("Content-Type", "application/json") 244 + |> request.set_method(http.Post) 245 + use resp <- promise.try_await(fetch.send(req)) 246 + use resp <- promise.try_await(fetch.read_text_body(resp)) 247 + promise.resolve(Ok(resp)) 248 + } 249 + |> promise.await(fn(a: Result(response.Response(String), fetch.FetchError)) { 250 + case a { 251 + Ok(b) -> { 252 + case 253 + json.decode( 254 + from: b.body, 255 + using: dynamic.decode3( 256 + FEPageServeResponse, 257 + dynamic.field("main", dynamic.string), 258 + dynamic.field("side", dynamic.string), 259 + dynamic.field("message", dynamic.list(dynamic.int)), 260 + ), 261 + ) 262 + { 263 + Ok(c) -> then(Ok(c)) 264 + Error(_) -> then(Error(Nil)) 265 + } 266 + } 267 + Error(_) -> then(Error(Nil)) 268 + } 269 + promise.resolve(Nil) 270 + }) 271 + } 272 + 273 + fn fetch_editor() { 274 + use presp <- fetch_page("editor", _) 275 + case presp { 276 + Ok(resp) -> { 277 + console.log("Page: " <> premixed.text_lightblue(string.inspect(resp))) 278 + let resp: FEPageServeResponse = resp 279 + let message_list = resp.message 280 + case 281 + bool.and( 282 + message_list |> list.contains(1) |> bool.negate, 283 + message_list |> list.contains(2) |> bool.negate, 284 + ) 285 + { 286 + True -> { 287 + // document.querySelector("div#posteditor").innerHTML = 288 + // response.data.main; 289 + // window.history.back(); 290 + { 291 + let assert Ok(a) = document.query_selector("div#posteditor") 292 + a 293 + } 294 + |> element.set_inner_html(resp.main) 295 + element_actions.go_back() 296 + Nil 297 + } 298 + False -> { 299 + // document.querySelector("div#posteditor").innerHTML = 300 + // errormsg; 301 + { 302 + let assert Ok(a) = document.query_selector("div#posteditor") 303 + a 304 + } 305 + |> element.set_inner_html( 306 + "<p class=\"w-full h-full text-black bg-white dark:text-white dark:bg-black\">Failed to load post editor.</p>", 307 + ) 308 + Nil 309 + } 310 + } 311 + } 312 + Error(_) -> { 313 + Nil 314 + } 315 + } 12 316 } 13 317 14 318 pub fn index_render() { 15 - web_io.println( 16 - "Detected you are on the " <> gleam_colours.text_faff_pink("first page") <> ".", 17 - ) 319 + console.log( 320 + "Detected you are on the " 321 + <> gleam_colours.text_faff_pink("first page") 322 + <> ".", 323 + ) 324 + } 325 + 326 + type SubPageList = 327 + Dict(String, SubPageMeta) 328 + 329 + type SubPageMeta { 330 + SubPageMeta( 331 + desktop: element.Element, 332 + mobile: element.Element, 333 + f: fn() -> Nil, 334 + location: String, 335 + navigator: Bool, 336 + ) 337 + } 338 + 339 + fn check_if_page_needs_to_be_switched(sub_page_list: SubPageList) { 340 + let hash = element_actions.get_window_location_hash() 341 + let p = document.body() |> element.get_attribute("data-current-page") 342 + case p { 343 + Ok(a) -> { 344 + case a == hash { 345 + True -> { 346 + // Do nothing. 347 + Nil 348 + } 349 + False -> { 350 + switch_subpage(hash, "URL change", sub_page_list) 351 + Nil 352 + } 353 + } 354 + } 355 + Error(_) -> { 356 + // If we get this, that means the initial page load has not been done yet. 357 + switch_subpage(hash, "Initial load", sub_page_list) 358 + Nil 359 + } 360 + } 361 + } 362 + 363 + fn switch_subpage(to_page: String, reason: String, sub_page_list: SubPageList) { 364 + let to = case to_page { 365 + "" -> { 366 + // The next line might cause some errors. 367 + // In the TypeScript version, the hash is only changed at the end of the function. 368 + // It also keeps the other URL parameters intact. 369 + element_actions.set_window_location_hash("home") 370 + "home" 371 + } 372 + _ -> { 373 + element_actions.set_window_location_hash(to_page) 374 + to_page 375 + } 376 + } 377 + let error_out = fn() { 378 + let assert Ok(a) = document.query_selector("main div#mainright") 379 + a |> element.set_inner_html("There was an error loading this page.") 380 + let assert Ok(a) = document.query_selector("main div#mainleft") 381 + a |> element.set_inner_html("") 382 + document.body() |> element.set_attribute("data-current-page", to) 383 + Nil 384 + } 385 + console.info("Switching page to " <> to <> ". Reason: " <> reason) 386 + dict.each(sub_page_list, fn(k, v) { 387 + case v { 388 + SubPageMeta(desktop, mobile, _, location, True) -> { 389 + case k == to { 390 + False -> { 391 + desktop 392 + |> element.set_attribute( 393 + "class", 394 + "px-3 py-2 text-sm font-medium bg-orange-200 border-2 rounded-md text-brown-800 dark:text-orange-200 border-emerald-600 dark:bg-yellow-700 dark:border-zinc-400 hover:bg-gray-700 hover:text-white", 395 + ) 396 + mobile 397 + |> element.set_attribute( 398 + "class", 399 + "block rounded-md px-3 py-2 text-base font-medium bg-orange-200 text-brown-800 dark:text-orange-200 border-emerald-600 dark:bg-yellow-700 dark:border-zinc-400 hover:bg-gray-700 hover:text-white", 400 + ) 401 + } 402 + True -> Nil 403 + } 404 + 405 + [desktop, mobile] 406 + |> list.each(fn(h) { 407 + case h |> element.dataset_get("listening") { 408 + Ok("true") -> Nil 409 + _ -> { 410 + h 411 + |> element.add_event_listener("click", fn(_) { 412 + switch_subpage(location, "Click event", sub_page_list) 413 + Nil 414 + }) 415 + h |> element.set_attribute("data-listening", "true") 416 + } 417 + } 418 + }) 419 + } 420 + SubPageMeta(_, _, _, _, False) -> { 421 + Nil 422 + } 423 + } 424 + }) 425 + case dict.get(sub_page_list, to) { 426 + Ok(SubPageMeta(desktop, mobile, f, location, _)) -> { 427 + mobile 428 + |> element.set_attribute( 429 + "class", 430 + "bg-red-400 dark:bg-red-900 text-white block rounded-md px-3 py-2 text-base font-medium", 431 + ) 432 + desktop 433 + |> element.set_attribute( 434 + "class", 435 + "border-2 px-3 py-2 text-sm font-medium text-white bg-gray-900 rounded-md", 436 + ) 437 + use resp <- fetch_page(location) 438 + case resp { 439 + Error(_) -> { 440 + console.error("Failed to fetch page." |> premixed.text_error_red()) 441 + error_out() 442 + } 443 + Ok(responza) -> { 444 + let respons = 445 + responza 446 + |> rendering.renders() 447 + let msg_list = respons.message 448 + case msg_list |> list.contains(1) { 449 + False -> { 450 + case msg_list |> list.contains(34) |> bool.negate { 451 + True -> { 452 + let assert Ok(a) = 453 + document.query_selector("main div#mainright") 454 + a |> element.set_inner_html(respons.main) 455 + case msg_list |> list.contains(33) |> bool.negate { 456 + True -> { 457 + let assert Ok(a) = 458 + document.query_selector("main div#mainleft") 459 + a |> element.set_inner_html(respons.side) 460 + } 461 + False -> Nil 462 + } 463 + } 464 + False -> { 465 + Nil 466 + } 467 + } 468 + } 469 + True -> { 470 + window.set_location( 471 + window.self(), 472 + "/login#" 473 + <> { 474 + let a = element_actions.get_window_location_hash() 475 + let assert Ok(b) = a |> string.split("?") |> list.first() 476 + b 477 + }, 478 + ) 479 + } 480 + } 481 + } 482 + } 483 + document.body() |> element.set_attribute("data-current-page", to) 484 + f() 485 + } 486 + Error(_) -> promise.resolve(error_out()) 487 + } 18 488 }
+4
nodemon.json
··· 1 + { 2 + "ext": "ts,rs,gleam,svg,handlebars,hb,pcss,css", 3 + "ignore": ["./*/build/**", "./backend/priv/generated/**"] 4 + }
+12 -5
package.json
··· 3 3 "packageManager": "bun@1.1.30+", 4 4 "devDependencies": { 5 5 "@biomejs/biome": "latest", 6 + "@types/bun": "latest", 7 + "tailwindcss": "latest", 8 + "autoprefixer": "latest", 9 + "postcss": "latest", 10 + "typescript": "^5.6.3", 11 + "postcss-cli": "^11.0.0", 12 + "clean-css-cli": "^5.6.3", 6 13 "bun": "latest" 7 14 }, 8 15 "dependencies": { ··· 10 17 "nodemon": "^3.1.7" 11 18 }, 12 19 "scripts": { 13 - "build": "bash ./build.sh --frontend-ts", 14 - "build-with-gleam": "bash ./build.sh --frontend-gleam", 20 + "build": "bash ./build.sh --frontend=typescript", 21 + "build-with-gleam": "bash ./build.sh --frontend=gleam", 15 22 "format": "bun run format:gleam && bun run format:ts && bun run minify:css", 16 23 "format:gleam": "echo \"Incomplete script.\"", 17 24 "format:ts": "bun x biome format . --write", 18 25 "minify": "bun run minify:css", 19 26 "start": "cd ./backend/ && gleam run ", 20 27 "dev-ts": "bun run build && bun run start", 21 - "dev-gleam": "bun run build-with-gleam && bun run start", 22 - "watch-ts": "bun x nodemon -e ts,gleam,svg,handlebars,hb,pcss,css --ignore './shared/build/**' --ignore './frontend/build/**' --ignore './backend/build/**' --ignore './backend/priv/generated/**' --exec bun run dev-ts ", 23 - "watch-gleam": "bun x nodemon -e mjs,gleam,svg,handlebars,hb,pcss,css --ignore './shared/build/**' --ignore './frontend/build/**' --ignore './backend/build/**' --ignore './backend/priv/generated/**' --exec bun run dev-gleam ", 28 + "dev-gleam": "bun tobundle.ts setup-prelude && bun run build-with-gleam && bun run start", 29 + "watch-ts": "bun x nodemon --exec bun run dev-ts", 30 + "watch-gleam": "bun x nodemon --exec bun run dev-gleam", 24 31 "clean": "rm -rf ./backend/node_modules/ && rm -rf ./frontend/node_modules/ && rm -rf ./frontend-ts/node_modules/ && rm -rf ./backend/priv/generated && mkdir -p ./backend/priv/generated/css && mkdir -p ./backend/priv/generated/js && cd backend && gleam clean && cd ../frontend && gleam clean && cd ../shared && gleam clean" 25 32 }, 26 33 "trustedDependencies": ["@biomejs/biome", "bun"]
+12
shared/src/lumina/shared/shared_fepage_com.gleam
··· 1 + // Copyright (c) 2024, MLC 'Strawmelonjuice' Bloeiman 2 + // Licensed under the BSD 3-Clause License. See the LICENSE file for more info. 3 + 4 + /// A request from the client to the server to serve a page. 5 + pub type FEPageServeRequest { 6 + FEPageServeRequest(location: String) 7 + } 8 + 9 + /// A response from the server to a request to serve a page. 10 + pub type FEPageServeResponse { 11 + FEPageServeResponse(main: String, side: String, message: List(Int)) 12 + }
-1
tobundle.ts
··· 1 - // @ts-ignore 2 1 import Bun from "bun"; 3 2 switch (process.argv[2]) { 4 3 case "js-1":
+1
watch.sh
··· 1 + watchexec -w . -e ts,rs,gleam,svg,handlebars,hb,pcss,css -r -- bash ./build.sh --run $@