diff --git a/.forgejo/workflows/build+release.yml b/.forgejo/workflows/build+release.yml
new file mode 100644
index 0000000..4f550d9
--- /dev/null
+++ b/.forgejo/workflows/build+release.yml
@@ -0,0 +1,146 @@
+name: 'Build and release binary file and packages'
+author: 'Neshura'
+
+on:
+  push:
+    tags:
+      - '[0-9]+.[0-9]+.[0-9]+'
+      - '[0-9]+.[0-9]+.[0-9]+-rc.[0-9]+'
+jobs:
+  test:
+    runs-on: docker
+    container: forgejo.neshweb.net/ci-docker-images/rust-node:latest
+    steps:
+      -
+        name: Add Clippy
+        run: rustup component add clippy
+      -
+        name: Checking Out Repository Code
+        uses: https://code.forgejo.org/actions/checkout@v3
+      -
+        name: Set Up Cargo Cache
+        uses: actions/cache@v3
+        with:
+          path: |
+            ~/.cargo/bin/
+            ~/.cargo/registry/index/
+            ~/.cargo/registry/cache/
+            ~/.cargo/git/db/
+            target/
+          key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
+      -
+        name: Run Clippy
+        run: cargo clippy
+      -
+        name: Check if Version in Cargo.toml matches Tag
+        run: |
+          VERSION=$(cat Cargo.toml | grep -E "(^|\|)version =" | cut -f2- -d= | tr -d \" | tr -d " ")
+          if test $VERSION != "${{  github.ref_name }}"; then 
+              echo "Expected Version is: '${{  github.ref_name }}' actual Version is: '$VERSION'"; 
+              exit 1
+          else 
+              echo "Version is: '$VERSION'"; 
+          fi
+  
+  build:
+    needs: test
+    if: success()
+    runs-on: docker
+    container: forgejo.neshweb.net/ci-docker-images/rust-node:latest
+    steps:
+      -
+        name: Checking Out Repository Code
+        uses: https://code.forgejo.org/actions/checkout@v3
+      -
+        name: Prepare build environment
+        run: mkdir dist
+      -
+        name: Compiling To Linux Target
+        run: |
+          cargo build -r
+          mv target/release/${{ github.event.repository.name }} dist/${{ github.event.repository.name }}-linux-amd64
+      -
+        name: Bundle .deb package
+        run: |
+          cargo deb
+          DEBIAN_REF=$(echo ${{ github.ref_name }} | tr - \~)
+          echo "DEBIAN_REF=$DEBIAN_REF" >> dist/build.env
+          DEBIAN_REV=-$(cat Cargo.toml | grep -E "(^|\|)revision =" | cut -f2- -d= | tr -d \" | tr -d " ")
+          echo "DEBIAN_REV=$DEBIAN_REV" >> dist/build.env
+          mv target/debian/${{ github.event.repository.name }}_"$DEBIAN_REF""$DEBIAN_REV"_amd64.deb dist/${{ github.event.repository.name }}_"$DEBIAN_REF""$DEBIAN_REV"_amd64.deb
+      -
+        name: Uploading Build Artifact
+        uses: actions/upload-artifact@v3
+        with:
+          name: release_blobs
+          path: dist
+          if-no-files-found: error
+  
+  upload-generic-package:
+    needs: build
+    if: success()
+    runs-on: docker
+    steps:
+      -
+        name: Downloading All Build Artifacts
+        uses: actions/download-artifact@v3
+      -
+        name: Upload Binary
+        run: |
+          echo 'curl -v --user ${{ secrets.FORGEJO_USERNAME }}:${{ secrets.FORGEJO_TOKEN }} \
+          --upload-file release_blobs/${{ github.event.repository.name }}-linux-amd64 \
+          https://forgejo.neshweb.net/api/packages/${{ secrets.FORGEJO_USERNAME }}/generic/${{ github.event.repository.name }}/${{ github.ref_name }}/${{ github.event.repository.name }}-linux-amd64'
+          curl -v --user ${{ secrets.FORGEJO_USERNAME }}:${{ secrets.FORGEJO_TOKEN }} \
+          --upload-file release_blobs/${{ github.event.repository.name }}-linux-amd64 \
+          https://forgejo.neshweb.net/api/packages/${{ secrets.FORGEJO_USERNAME }}/generic/${{ github.event.repository.name }}/${{ github.ref_name }}/${{ github.event.repository.name }}-linux-amd64
+  
+  upload-debian-package:
+    needs: build
+    if: success()
+    runs-on: docker
+    steps:
+      -
+        name: Downloading All Build Artifacts
+        uses: actions/download-artifact@v3
+      -
+        name: Upload Debian Package to staging
+        run: |
+          source release_blobs/build.env
+          echo 'curl -v --user ${{ secrets.FORGEJO_USERNAME }}:${{ secrets.FORGEJO_TOKEN }} \
+          --upload-file release_blobs/${{ github.event.repository.name }}_'"$DEBIAN_REF""$DEBIAN_REV"'_amd64.deb \
+          https://forgejo.neshweb.net/api/packages/${{ secrets.FORGEJO_USERNAME }}/debian/pool/bookworm/staging/upload'
+          curl -v --user ${{ secrets.FORGEJO_USERNAME }}:${{ secrets.FORGEJO_TOKEN }} \
+          --upload-file release_blobs/${{ github.event.repository.name }}_"$DEBIAN_REF""$DEBIAN_REV"_amd64.deb \
+          https://forgejo.neshweb.net/api/packages/${{ secrets.FORGEJO_USERNAME }}/debian/pool/bookworm/staging/upload
+      -
+        name: Upload Debian Package to main
+        if: (! contains(github.ref_name, '-rc'))
+        run: |
+          source release_blobs/build.env
+          echo 'curl -v --user ${{ secrets.FORGEJO_USERNAME }}:${{ secrets.FORGEJO_TOKEN }} \
+          --upload-file release_blobs/${{ github.event.repository.name }}_'"$DEBIAN_REF""$DEBIAN_REV"'_amd64.deb \
+          https://forgejo.neshweb.net/api/packages/${{ secrets.FORGEJO_USERNAME }}/debian/pool/bookworm/main/upload'
+          curl -v --user ${{ secrets.FORGEJO_USERNAME }}:${{ secrets.FORGEJO_TOKEN }} \
+          --upload-file release_blobs/${{ github.event.repository.name }}_"$DEBIAN_REF""$DEBIAN_REV"_amd64.deb \
+          https://forgejo.neshweb.net/api/packages/${{ secrets.FORGEJO_USERNAME }}/debian/pool/bookworm/main/upload
+  
+  create-release:
+    needs: build
+    if: success()
+    runs-on: docker
+    steps:
+      -
+        name: Downloading All Build Artifacts
+        uses: actions/download-artifact@v3
+      -
+        name: Filter out env files
+        run: rm release_blobs/build.env
+      -
+        name: Release New Version
+        uses: actions/forgejo-release@v2
+        with:
+          direction: upload
+          url: https://forgejo.neshweb.net
+          release-dir: release_blobs
+          token: ${{ secrets.FORGEJO_TOKEN }}
+          tag: ${{ github.ref_name }}
\ No newline at end of file
diff --git a/.forgejo/workflows/pull-requests.yml b/.forgejo/workflows/pull-requests.yml
new file mode 100644
index 0000000..2e53c8e
--- /dev/null
+++ b/.forgejo/workflows/pull-requests.yml
@@ -0,0 +1,67 @@
+name: 'Build binary file and bundle packages'
+author: 'Neshura'
+
+on:
+  pull_request:
+    branches:
+      - main
+
+jobs:
+  test:
+    runs-on: docker
+    container: forgejo.neshweb.net/ci-docker-images/rust-node:latest
+    steps:
+      -
+        name: Add Clippy
+        run: rustup component add clippy
+      -
+        name: Checking Out Repository Code
+        uses: https://code.forgejo.org/actions/checkout@v3
+      -
+        name: Set Up Cargo Cache
+        uses: actions/cache@v3
+        with:
+          path: |
+            ~/.cargo/bin/
+            ~/.cargo/registry/index/
+            ~/.cargo/registry/cache/
+            ~/.cargo/git/db/
+            target/
+          key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
+      -
+        name: Run Clippy
+        run: cargo clippy
+
+  build:
+    needs: test
+    if: success()
+    runs-on: docker
+    container: forgejo.neshweb.net/ci-docker-images/rust-node:latest
+    steps:
+      -
+        name: Checking Out Repository Code
+        uses: https://code.forgejo.org/actions/checkout@v3
+      -
+        name: Prepare build environment
+        run: mkdir dist
+      -
+        name: Compiling To Linux Target
+        run: |
+          cargo build -r
+          mv target/release/${{ github.event.repository.name }} dist/${{ github.event.repository.name }}-linux-amd64
+      -
+        name: Bundle .deb package
+        run: |
+          cargo deb
+          DEBIAN_REF=$(cat Cargo.toml | grep -E "(^|\|)version =" | cut -f2- -d= | tr -d \" | tr -d " " | tr - \~)
+          echo "DEBIAN_REF=$DEBIAN_REF" >> dist/build.env
+          DEBIAN_REV=-$(cat Cargo.toml | grep -E "(^|\|)revision =" | cut -f2- -d= | tr -d \" | tr -d " ")
+          echo "DEBIAN_REV=$DEBIAN_REV" >> dist/build.env
+          mv target/debian/${{ github.event.repository.name }}_"$DEBIAN_REF""$DEBIAN_REV"_amd64.deb dist/${{ github.event.repository.name }}_"$DEBIAN_REF""$DEBIAN_REV"_amd64.deb
+      -
+        name: Uploading Build Artifact
+        uses: actions/upload-artifact@v3
+        with:
+          name: release_blobs
+          path: dist
+          if-no-files-found: error
\ No newline at end of file
diff --git a/.forgejo/workflows/test.yml b/.forgejo/workflows/test.yml
new file mode 100644
index 0000000..920821d
--- /dev/null
+++ b/.forgejo/workflows/test.yml
@@ -0,0 +1,34 @@
+name: 'Run Tests on Code'
+author: 'Neshura'
+
+on:
+  push:
+    tags-ignore:
+      - '**'
+    branches:
+      - '**'
+jobs:
+  run-tests:
+    runs-on: docker
+    container: forgejo.neshweb.net/ci-docker-images/rust-node:latest
+    steps:
+      -
+        name: Add Clippy
+        run: rustup component add clippy
+      -
+        name: Checking Out Repository Code
+        uses: https://code.forgejo.org/actions/checkout@v3
+      -
+        name: Set Up Cargo Cache
+        uses: actions/cache@v3
+        with:
+          path: |
+            ~/.cargo/bin/
+            ~/.cargo/registry/index/
+            ~/.cargo/registry/cache/
+            ~/.cargo/git/db/
+            target/
+          key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
+      -
+        name: Run Clippy
+        run: cargo clippy
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..fedaa2b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+/target
+.env
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..b58b603
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,5 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
diff --git a/.idea/material_theme_project_new.xml b/.idea/material_theme_project_new.xml
new file mode 100644
index 0000000..94c4557
--- /dev/null
+++ b/.idea/material_theme_project_new.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="MaterialThemeProjectNewConfig">
+    <option name="metadata">
+      <MTProjectMetadataState>
+        <option name="userId" value="35e9767a:19031e016a2:-7f1b" />
+      </MTProjectMetadataState>
+    </option>
+  </component>
+</project>
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..12926f6
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectInspectionProfilesVisibleTreeState">
+    <entry key="Project Default">
+      <profile-state>
+        <expanded-state>
+          <State>
+            <id>AccessibilityHTML</id>
+          </State>
+          <State>
+            <id>Code style issuesJavaScript and TypeScript</id>
+          </State>
+          <State>
+            <id>ES2015 migration aidsJavaScript and TypeScript</id>
+          </State>
+          <State>
+            <id>GitHub actions</id>
+          </State>
+          <State>
+            <id>HTML</id>
+          </State>
+          <State>
+            <id>JavaScript and TypeScript</id>
+          </State>
+          <State>
+            <id>RegExp</id>
+          </State>
+          <State>
+            <id>Rust</id>
+          </State>
+          <State>
+            <id>Switch statement issuesJavaScript and TypeScript</id>
+          </State>
+          <State>
+            <id>TypeScriptJavaScript and TypeScript</id>
+          </State>
+          <State>
+            <id>XML</id>
+          </State>
+          <State>
+            <id>XSLT</id>
+          </State>
+        </expanded-state>
+      </profile-state>
+    </entry>
+  </component>
+</project>
\ No newline at end of file
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..ff1f010
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,1026 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "addr2line"
+version = "0.22.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678"
+dependencies = [
+ "gimli",
+]
+
+[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
+name = "android-tzdata"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "async-trait"
+version = "0.1.81"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
+
+[[package]]
+name = "axum"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf"
+dependencies = [
+ "async-trait",
+ "axum-core",
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "http-body-util",
+ "hyper",
+ "hyper-util",
+ "itoa",
+ "matchit",
+ "memchr",
+ "mime",
+ "percent-encoding",
+ "pin-project-lite",
+ "rustversion",
+ "serde",
+ "serde_json",
+ "serde_path_to_error",
+ "serde_urlencoded",
+ "sync_wrapper 1.0.1",
+ "tokio",
+ "tower",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "axum-core"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3"
+dependencies = [
+ "async-trait",
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "http-body-util",
+ "mime",
+ "pin-project-lite",
+ "rustversion",
+ "sync_wrapper 0.1.2",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "backtrace"
+version = "0.3.73"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a"
+dependencies = [
+ "addr2line",
+ "cc",
+ "cfg-if",
+ "libc",
+ "miniz_oxide",
+ "object",
+ "rustc-demangle",
+]
+
+[[package]]
+name = "bitflags"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
+
+[[package]]
+name = "bumpalo"
+version = "3.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
+
+[[package]]
+name = "bytes"
+version = "1.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50"
+
+[[package]]
+name = "cc"
+version = "1.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "chrono"
+version = "0.4.38"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
+dependencies = [
+ "android-tzdata",
+ "iana-time-zone",
+ "js-sys",
+ "num-traits",
+ "wasm-bindgen",
+ "windows-targets",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
+
+[[package]]
+name = "dotenv"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
+
+[[package]]
+name = "errno"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
+dependencies = [
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "form_urlencoded"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
+dependencies = [
+ "percent-encoding",
+]
+
+[[package]]
+name = "futures"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
+
+[[package]]
+name = "futures-executor"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
+
+[[package]]
+name = "futures-macro"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "futures-sink"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
+
+[[package]]
+name = "futures-task"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
+
+[[package]]
+name = "futures-util"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-macro",
+ "futures-sink",
+ "futures-task",
+ "memchr",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+]
+
+[[package]]
+name = "gimli"
+version = "0.29.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
+
+[[package]]
+name = "hermit-abi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
+
+[[package]]
+name = "http"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258"
+dependencies = [
+ "bytes",
+ "fnv",
+ "itoa",
+]
+
+[[package]]
+name = "http-body"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
+dependencies = [
+ "bytes",
+ "http",
+]
+
+[[package]]
+name = "http-body-util"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f"
+dependencies = [
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "httparse"
+version = "1.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9"
+
+[[package]]
+name = "httpdate"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
+
+[[package]]
+name = "hyper"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05"
+dependencies = [
+ "bytes",
+ "futures-channel",
+ "futures-util",
+ "http",
+ "http-body",
+ "httparse",
+ "httpdate",
+ "itoa",
+ "pin-project-lite",
+ "smallvec",
+ "tokio",
+]
+
+[[package]]
+name = "hyper-util"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9"
+dependencies = [
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "hyper",
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "iana-time-zone"
+version = "0.1.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "wasm-bindgen",
+ "windows-core",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
+
+[[package]]
+name = "js-sys"
+version = "0.3.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "kavita-web-manager-api"
+version = "0.1.0"
+dependencies = [
+ "axum",
+ "chrono",
+ "dotenv",
+ "futures",
+ "log",
+ "parking_lot",
+ "systemd-journal-logger",
+ "tokio",
+ "tokio-util",
+ "urlencoding",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.155"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
+
+[[package]]
+name = "lock_api"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
+dependencies = [
+ "value-bag",
+]
+
+[[package]]
+name = "matchit"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
+
+[[package]]
+name = "memchr"
+version = "2.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+
+[[package]]
+name = "mime"
+version = "0.3.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
+
+[[package]]
+name = "miniz_oxide"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08"
+dependencies = [
+ "adler",
+]
+
+[[package]]
+name = "mio"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4"
+dependencies = [
+ "hermit-abi",
+ "libc",
+ "wasi",
+ "windows-sys",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "object"
+version = "0.36.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "27b64972346851a39438c60b341ebc01bba47464ae329e55cf343eb93964efd9"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+
+[[package]]
+name = "parking_lot"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "windows-targets",
+]
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
+
+[[package]]
+name = "pin-project"
+version = "1.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3"
+dependencies = [
+ "pin-project-internal",
+]
+
+[[package]]
+name = "pin-project-internal"
+version = "1.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.86"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
+
+[[package]]
+name = "rustix"
+version = "0.38.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
+dependencies = [
+ "bitflags",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
+
+[[package]]
+name = "ryu"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
+name = "serde"
+version = "1.0.204"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.204"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.122"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da"
+dependencies = [
+ "itoa",
+ "memchr",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "serde_path_to_error"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6"
+dependencies = [
+ "itoa",
+ "serde",
+]
+
+[[package]]
+name = "serde_urlencoded"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
+dependencies = [
+ "form_urlencoded",
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "signal-hook-registry"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "slab"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
+
+[[package]]
+name = "socket2"
+version = "0.5.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c"
+dependencies = [
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.72"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "sync_wrapper"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
+
+[[package]]
+name = "sync_wrapper"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394"
+
+[[package]]
+name = "systemd-journal-logger"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5f3848dd723f2a54ac1d96da793b32923b52de8dfcced8722516dac312a5b2a"
+dependencies = [
+ "log",
+ "rustix",
+]
+
+[[package]]
+name = "tokio"
+version = "1.39.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1"
+dependencies = [
+ "backtrace",
+ "bytes",
+ "libc",
+ "mio",
+ "parking_lot",
+ "pin-project-lite",
+ "signal-hook-registry",
+ "socket2",
+ "tokio-macros",
+ "windows-sys",
+]
+
+[[package]]
+name = "tokio-macros"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tokio-util"
+version = "0.7.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "futures-sink",
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "tower"
+version = "0.4.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
+dependencies = [
+ "futures-core",
+ "futures-util",
+ "pin-project",
+ "pin-project-lite",
+ "tokio",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "tower-layer"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0"
+
+[[package]]
+name = "tower-service"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
+
+[[package]]
+name = "tracing"
+version = "0.1.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
+dependencies = [
+ "log",
+ "pin-project-lite",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
+dependencies = [
+ "once_cell",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
+name = "urlencoding"
+version = "2.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
+
+[[package]]
+name = "value-bag"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a84c137d37ab0142f0f2ddfe332651fdbf252e7b7dbb4e67b6c1f1b2e925101"
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
+
+[[package]]
+name = "windows-core"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..a933bbc
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,34 @@
+[package]
+authors = ["Neshura"]
+name = "kavita-web-manager-api"
+version = "0.1.0"
+edition = "2021"
+description = "API Backend For Easier Uploading To Kavita"
+license = "GPL-3.0-or-later"
+
+
+[package.metadata.deb]
+extended-description = "API Backend For Easier Uploading To Kavita"
+maintainer-scripts = "debian/"
+revision = "1"
+depends = ["libc6", "libssl3", "systemd"]
+assets = [
+    [
+        "target/release/kavita-web-manager-api",
+        "/usr/local/bin/kavita-web-manager-api",
+        "755",
+    ]
+]
+systemd-units = { enable = false }
+
+[dependencies]
+axum = "0.7"
+chrono = "0.4"
+dotenv = "0.15"
+futures = "0.3"
+log = "0.4"
+parking_lot = "0.12"
+systemd-journal-logger = "2.1"
+tokio = {version = "1.39", features = ["full"]}
+tokio-util = { version = "0.7", features = ["io"] }
+urlencoding = "2.1"
diff --git a/debian/kavita-web-manager-api.service b/debian/kavita-web-manager-api.service
new file mode 100644
index 0000000..d1114bb
--- /dev/null
+++ b/debian/kavita-web-manager-api.service
@@ -0,0 +1,13 @@
+[Unit]
+Description="Application for automatically updating Cloudflare DNS records"
+After=syslog.target
+After=network-online.target
+
+[Service]
+Type=simple
+ExecStart=/usr/local/bin/kavita-web-manager-api
+Restart=always
+RestartSec=3
+
+[Install]
+WantedBy=multi-user.target
\ No newline at end of file
diff --git a/src/logging.rs b/src/logging.rs
new file mode 100644
index 0000000..2aecde9
--- /dev/null
+++ b/src/logging.rs
@@ -0,0 +1,114 @@
+use std::collections::{HashMap, VecDeque};
+use chrono::{DateTime, Utc};
+use log::{Level};
+use systemd_journal_logger::connected_to_journal;
+
+#[derive(Clone)]
+pub struct Logging {
+    mem_log: HashMap<Level, VecDeque<LogEvent>>,
+    mem_log_lengths: HashMap<Level, i8>,
+}
+
+impl Logging {
+    pub fn new(log_lengths: Option<HashMap<Level, i8>>) -> Self {
+        let mut mem_log = HashMap::new();
+        let mut mem_log_lengths = HashMap::new();
+
+        Level::iter().for_each(|level| {
+            let mem_vec = VecDeque::new();
+            mem_log.insert(level, mem_vec);
+
+            let length = match level {
+                Level::Error => -1,
+                Level::Warn => 40,
+                Level::Info => 20,
+                Level::Debug => 10,
+                Level::Trace => 5,
+            };
+            mem_log_lengths.insert(level, length);
+        });
+        
+        if let Some(lengths) = log_lengths {
+            lengths.iter().for_each(|(level, length)| {
+                mem_log_lengths.insert(*level, *length);
+            });
+        }
+
+        Self {
+            mem_log,
+            mem_log_lengths
+        }
+    }
+    pub fn debug(&mut self, msg: String) {
+        let msg = format!("[DEBUG] {msg}");
+        match connected_to_journal() {
+            true => log::debug!("{msg}"),
+            false => println!("{msg}"),
+        }
+        self.mem_log(Level::Debug, Some(msg));
+    }
+
+    pub fn info(&mut self, msg: String) {
+        let msg = format!("[INFO] {msg}");
+        match connected_to_journal() {
+            true => log::info!("{msg}"),
+            false => println!("{msg}"),
+        }
+        self.mem_log(Level::Info, Some(msg));
+    }
+
+    pub fn warn(&mut self, msg: String) {
+        let msg = format!("[WARN] {msg}");
+        match connected_to_journal() {
+            true => log::warn!("{msg}"),
+            false => println!("{msg}"),
+        }
+        self.mem_log(Level::Warn, Some(msg));
+    }
+
+    pub fn error(&mut self, msg: String) {
+        let msg = format!("[ERROR] {msg}");
+        match connected_to_journal() {
+            true => log::error!("{msg}"),
+            false => eprintln!("{msg}"),
+        }
+        self.mem_log(Level::Error, Some(msg));
+    }
+
+    pub fn get_mem_log(&mut self, level: Level) -> VecDeque<LogEvent> {
+        self.mem_log(level, None)
+    }
+
+    fn mem_log(&mut self, level: Level, msg: Option<String>) -> VecDeque<LogEvent> {
+        let max_len = self.mem_log_lengths.get(&level).expect("All Levels Should Have Lengths Initialized");
+        let list = self.mem_log.get_mut(&level).expect("All Levels Should Have Empty Lists Initialized");
+        
+        if let Some(msg) = msg {
+            let now = Utc::now();
+            let log_event = LogEvent::new(now, msg);
+            list.push_back(log_event);
+            if *max_len != -1 {
+                while list.len() > *max_len as usize {
+                    list.pop_front();
+                }
+            }
+        }
+
+        list.clone()
+    }
+}
+
+#[derive(Clone)]
+pub(crate) struct LogEvent {
+    pub date: DateTime<Utc>,
+    pub text: String,
+}
+
+impl LogEvent {
+    pub fn new(time: DateTime<Utc>, message: String) -> Self {
+        Self {
+            date: time,
+            text: message
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..71f2297
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,216 @@
+mod logging;
+
+use std::collections::HashMap;
+use std::path::{Path, PathBuf};
+use axum::body::BodyDataStream;
+use axum::extract::{Request, State};
+use axum::http::StatusCode;
+use axum::Router;
+use axum::routing::post;
+use dotenv::{dotenv, var};
+use futures::TryStreamExt;
+use log::LevelFilter;
+use systemd_journal_logger::JournalLog;
+use tokio::fs::File;
+use tokio::{fs, io};
+use tokio::io::BufWriter;
+use tokio_util::io::StreamReader;
+use urlencoding::decode;
+use crate::logging::Logging;
+
+#[derive(Clone)]
+struct App {
+    log: Logging,
+    directories: HashMap<String, String>
+}
+
+impl App {
+    pub fn init_directories(&mut self) {
+        let root_dir = match var("ROOT_DIRECTORY") {
+            Ok(dir) => {
+                self.log.info(format!("ROOT_DIRECTORY set to '{dir}'"));
+                dir
+            }
+            Err(e) => {
+                self.log.error(format!("ROOT_DIRECTORY not set: {e}. Aborting."));
+                panic!("ROOT_DIRECTORY not set: {e}. Aborting.");
+            }
+        };
+
+        let novel_dir = match var("NOVEL_DIRECTORY") {
+            Ok(dir) => {
+                self.log.info(format!("NOVEL_DIRECTORY set to '{root_dir}/{dir}'"));
+                format!("{root_dir}/{dir}")
+            }
+            Err(e) => {
+                self.log.error(format!("NOVEL_DIRECTORY not set: {e}. Defaulting to '{root_dir}/novels'."));
+                format!("{root_dir}/novels")
+            }
+        };
+
+        self.directories.insert("Novel".to_owned(), novel_dir);
+
+        let manga_dir = match var("MANGA_DIRECTORY") {
+            Ok(dir) => {
+                self.log.info(format!("MANGA_DIRECTORY set to '{root_dir}/{dir}'"));
+                format!("{root_dir}/{dir}")
+            }
+            Err(e) => {
+                self.log.error(format!("MANGA_DIRECTORY not set: {e}. Defaulting to '{root_dir}/manga'."));
+                format!("{root_dir}/manga")
+            }
+        };
+
+        self.directories.insert("Manga".to_owned(), manga_dir);
+
+        let hentai_dir = match var("HENTAI_DIRECTORY") {
+            Ok(dir) => {
+                self.log.info(format!("HENTAI_DIRECTORY set to '{root_dir}/{dir}'"));
+                format!("{root_dir}/{dir}")
+            }
+            Err(e) => {
+                self.log.error(format!("HENTAI_DIRECTORY not set: {e}. Defaulting to '{root_dir}/hentai'."));
+                format!("{root_dir}/hentai")
+            }
+        };
+
+        self.directories.insert("Hentai".to_owned(), hentai_dir);
+    }
+}
+
+#[tokio::main]
+async fn main() {
+    dotenv().expect("Failed to init dotenv");
+
+    JournalLog::new()
+        .expect("Systemd-Logger crate error")
+        .install()
+        .expect("Systemd-Logger crate error");
+
+    match var("LOG_LEVEL") {
+        Ok(level) => {
+            match level.as_str() {
+                "debug" => log::set_max_level(LevelFilter::Debug),
+                "info" => log::set_max_level(LevelFilter::Info),
+                _ => log::set_max_level(LevelFilter::Info),
+            }
+        }
+        _ => log::set_max_level(LevelFilter::Info),
+    }
+
+    let mut app = App {
+        log: Logging::new(None),
+        directories: HashMap::new(),
+    };
+
+    app.init_directories();
+
+    let api = Router::new()
+        .route("/upload", post(|State(mut state): State<App>, request: Request| async move {
+            upload_file(&mut state, request).await;
+        }))
+        .with_state(app);
+
+    let listener = tokio::net::TcpListener::bind("[::]:3000").await.unwrap();
+    axum::serve(listener, api).await.unwrap();
+}
+
+#[derive(Debug)]
+struct FilePath {
+    format: String,
+    series: String,
+    volume: String,
+    extension: String
+}
+
+impl FilePath {
+    fn new() -> Self {
+        Self {
+            format: "".to_owned(),
+            series: "".to_owned(),
+            volume: "".to_owned(),
+            extension: "".to_owned()
+        }
+    }
+
+    fn check_valid(&self) -> bool {
+        if self.format == "" || self.series == "" || self.volume == "" || self.extension == "" {
+            return false
+        }
+        return true
+    }
+
+    fn to_pathbuf(&self) -> PathBuf {
+        Path::new(format!("{}/{}/{}/{}.{}", self.format, self.series, self.volume, self.volume, self.extension).as_str()).to_path_buf()
+    }
+}
+
+async fn upload_file(state: &mut App, request: Request) {
+    let params_raw: Vec<&str> = request.uri().query().unwrap().split('&').collect();
+    let mut file = FilePath::new();
+    params_raw.iter().for_each(|param| {
+        let split: Vec<&str> = param.split('=').collect();
+        state.log.info(format!("Parsing Parameter Key-Value Pair '{param}'"));
+        match split[0] {
+            "format" => {
+                file.format.clone_from(state.directories.get(split[1]).expect("Assume Valid Format Was Provided"));
+            },
+            "series" => {
+                file.series = decode(split[1]).expect("UTF-8").to_string();
+            },
+            "volume" => {
+                file.volume = decode(split[1]).expect("UTF-8").to_string();
+            },
+            k => {
+                state.log.warn(format!("Parameter {k} is not known and will be ignored"));
+            }
+        }
+    });
+
+    let content_type = request.headers().get("Content-Type").expect("Content Type Should Have Been provided").to_str().expect("Content Type Should Be String");
+
+    file.extension = match content_type {
+        "application/epub+zip" => "epub".to_owned(),
+        "application/comic+zip" => "cbz".to_owned(),
+        "application/pdf" => "pdf".to_owned(),
+        ct => {
+            state.log.error(format!("Invalid Content Type '{ct}' Provided, Aborting"));
+            panic!("Invalid Content Type '{ct}'")
+        }
+    };
+
+    println!("{:#?}", file);
+
+    if !file.check_valid() {
+        //return Err((StatusCode::BAD_REQUEST, "Format not specified".to_owned()));
+    }
+
+    let pathbuf = file.to_pathbuf();
+    state.log.info(format!("File Path '{}'", pathbuf.clone().display()));
+
+    let file_stream = request.into_body().into_data_stream();
+    if let Err(e) = stream_to_file(&pathbuf, file_stream).await {
+        state.log.error(format!("{}: {}", e.0, e.1));
+    };
+}
+
+async fn stream_to_file(path: &PathBuf, stream: BodyDataStream) -> Result<(), (StatusCode, String)>
+{
+    if !Path::exists(path.parent().unwrap()) {
+        fs::create_dir_all(path.parent().unwrap()).await.expect("Unable to Create Path");
+    }
+
+    async {
+        let body_with_io_error = stream.map_err(|err| io::Error::new(io::ErrorKind::Other, err));
+        let body_reader = StreamReader::new(body_with_io_error);
+        futures::pin_mut!(body_reader);
+
+        let mut file = BufWriter::new(File::create(path).await?);
+
+        io::copy(&mut body_reader, &mut file).await?;
+
+        Ok::<_, io::Error>(())
+    }
+        .await
+        .map_err(|err| (StatusCode::INTERNAL_SERVER_ERROR, err.to_string()))
+}