diff --git a/.classpath b/.classpath deleted file mode 100644 index 66f8050..0000000 --- a/.classpath +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/.gitignore b/.gitignore index 24ff440..93747ab 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,9 @@ -# Ignore Gradle project-specific cache directory -.gradle +# Ignore cargo build outputs +actix/target -# Ignore Gradle build output directory -build -.idea/ - -local.properties -url.iml -urls.csv -.env +# Ignore SQLite file urls.sqlite + +# Ignore irrelevant dotfiles +.vscode/ +.directory diff --git a/.project b/.project deleted file mode 100644 index db7a7ad..0000000 --- a/.project +++ /dev/null @@ -1,34 +0,0 @@ - - - url - Project url created by Buildship. - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.buildship.core.gradleprojectbuilder - - - - - - org.eclipse.jdt.core.javanature - org.eclipse.buildship.core.gradleprojectnature - - - - 1667542035221 - - 30 - - org.eclipse.core.resources.regexFilterMatcher - node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ - - - - diff --git a/.settings/org.eclipse.buildship.core.prefs b/.settings/org.eclipse.buildship.core.prefs deleted file mode 100644 index a6d1fd9..0000000 --- a/.settings/org.eclipse.buildship.core.prefs +++ /dev/null @@ -1,13 +0,0 @@ -arguments=--init-script /home/sintan/.config/Code/User/globalStorage/redhat.java/1.12.0/config_linux/org.eclipse.osgi/51/0/.cp/gradle/init/init.gradle --init-script /home/sintan/.config/Code/User/globalStorage/redhat.java/1.12.0/config_linux/org.eclipse.osgi/51/0/.cp/gradle/protobuf/init.gradle -auto.sync=false -build.scans.enabled=false -connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER) -connection.project.dir= -eclipse.preferences.version=1 -gradle.user.home= -java.home=/usr/lib/jvm/java-19-openjdk -jvm.arguments= -offline.mode=false -override.workspace.settings=true -show.console.view=true -show.executions.view=true diff --git a/Dockerfile b/Dockerfile index 84d0f62..4787d46 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,14 +1,27 @@ -FROM gradle:jdk17-alpine AS build -COPY --chown=gradle:gradle . /home/gradle/src -WORKDIR /home/gradle/src -RUN gradle fatJar --no-daemon +FROM rust:1 as build +RUN cargo install cargo-build-deps -FROM openjdk:17-alpine +RUN cargo new --bin simply-shorten +WORKDIR /simply-shorten -EXPOSE 4567 +COPY ./actix/Cargo.toml . +COPY ./actix/Cargo.lock . -RUN mkdir /app +RUN cargo build-deps --release -COPY --from=build /home/gradle/src/build/libs/*.jar /app/application.jar +COPY ./actix/src ./src +COPY ./actix/resources ./resources -ENTRYPOINT ["java", "-jar","/app/application.jar"] +RUN cargo build --release + +FROM frolvlad/alpine-glibc:latest + +EXPOSE 2000 +RUN apk add sqlite-libs + +WORKDIR /opt + +COPY --from=build /simply-shorten/target/release/simply-shorten /opt/simply-shorten +COPY --from=build /simply-shorten/resources /opt/resources + +CMD ["./simply-shorten"] diff --git a/README.md b/README.md index bd91456..9afd850 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ![Logo](src/main/resources/public/assets/favicon-32.png) Simply Shorten +# ![Logo](actix/resources/assets/favicon-32.png) Simply Shorten # What is it? A simple selfhosted URL shortener with no unnecessary features. @@ -28,7 +28,7 @@ unnecessary features, or they didn't have all the features I wanted. generate short links locally. - Links are stored in an SQLite database. - Available as a Docker container. -- Backend written in Java using [Spark Java](http://sparkjava.com/), frontend +- Backend written in Rust using [Actix](https://actix.rs/), frontend written in plain HTML and vanilla JS, using [Pure CSS](https://purecss.io/) for styling. @@ -62,19 +62,10 @@ Clone this repository ``` git clone https://gitlab.com/SinTan1729/simply-shorten ``` -Note that Gradle 6.x.x and JDK 11 are required. Other versions are not tested -### 1. Build the `.jar` file -``` -gradle build --no-daemon -``` -The `--no-daemon` option means that gradle should exit as soon as the build is -finished. Without it, gradle would still be running in the background -in order to speed up future builds. ### 2. Set environment variables ```bash # Required for authentication -export username= export password= # Sets where the database exists. Can be local or remote (optional) export db_url= # Default: './urls.sqlite' @@ -83,9 +74,10 @@ export db_url= # Default: './urls.sqlite' export site_url= ``` -### 3. Run it +### 3. Build and run it ``` -java -jar build/libs/url.jar +cd actix +cargo run ``` You can optionally set the port the server listens on by appending `--port=[port]` ### 4. Navigate to `http://localhost:4567` in your browser, add links as you wish. @@ -99,7 +91,6 @@ docker build . -t simply-shorten:latest 1. Run the image ``` docker run -p 4567:4567 - -d url:latest -e username="username" -e password="password" -d simply-shorten:latest @@ -127,20 +118,16 @@ docker run -p 4567:4567 \ ``` ## Disable authentication -As requested in #5, it is possible to completely disable the authentication. -This if not recommended, as it will allow anyone to create new links and delete +It's not possible to completely disable authentication. It's rather easy to implement +but there's literally no point. Rather, for testing purposes, you can omit the password +environment variable, and any provided password should work. + +This if not recommended in actual use however, as it will allow anyone to create new links and delete old ones. This might not seem like a bad idea, until you have hundreds of links pointing to illegal content. Since there are no logs, it's impossible to prove that those links aren't created by you. -If you still want to do it, then you need to set an environment variable to -an exact value: -``` -INSECURE_DISABLE_PASSWORD=I_KNOW_ITS_BAD -``` -Any other value will not work. - ## Notes -- This is a fork of [this project](https://gitlab.com/draganczukp/simply-shorten). +- It started as a fork of [this project](https://gitlab.com/draganczukp/simply-shorten). - The list of adjectives and names used for random short url generation is a modified version of [this list used by docker](https://github.com/moby/moby/blob/master/pkg/namesgenerator/names-generator.go). \ No newline at end of file diff --git a/actix/.directory b/actix/.directory new file mode 100644 index 0000000..e67a114 --- /dev/null +++ b/actix/.directory @@ -0,0 +1,3 @@ +[Dolphin] +Timestamp=2023,4,2,17,52,37.922 +Version=4 diff --git a/actix/Cargo.lock b/actix/Cargo.lock new file mode 100644 index 0000000..6cc0065 --- /dev/null +++ b/actix/Cargo.lock @@ -0,0 +1,1487 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "actix-codec" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a7559404a7f3573127aab53c08ce37a6c6a315c374a31070f3c91cd1b4a7fe" +dependencies = [ + "bitflags 1.3.2", + "bytes", + "futures-core", + "futures-sink", + "log", + "memchr", + "pin-project-lite", + "tokio", + "tokio-util", +] + +[[package]] +name = "actix-files" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d832782fac6ca7369a70c9ee9a20554623c5e51c76e190ad151780ebea1cf689" +dependencies = [ + "actix-http", + "actix-service", + "actix-utils", + "actix-web", + "askama_escape", + "bitflags 1.3.2", + "bytes", + "derive_more", + "futures-core", + "http-range", + "log", + "mime", + "mime_guess", + "percent-encoding", + "pin-project-lite", +] + +[[package]] +name = "actix-http" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2079246596c18b4a33e274ae10c0e50613f4d32a4198e09c7b93771013fed74" +dependencies = [ + "actix-codec", + "actix-rt", + "actix-service", + "actix-utils", + "ahash 0.8.3", + "base64 0.21.0", + "bitflags 1.3.2", + "brotli", + "bytes", + "bytestring", + "derive_more", + "encoding_rs", + "flate2", + "futures-core", + "h2", + "http", + "httparse", + "httpdate", + "itoa", + "language-tags", + "local-channel", + "mime", + "percent-encoding", + "pin-project-lite", + "rand", + "sha1", + "smallvec", + "tokio", + "tokio-util", + "tracing", + "zstd", +] + +[[package]] +name = "actix-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "465a6172cf69b960917811022d8f29bc0b7fa1398bc4f78b3c466673db1213b6" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "actix-router" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66ff4d247d2b160861fa2866457e85706833527840e4133f8f49aa423a38799" +dependencies = [ + "bytestring", + "http", + "regex", + "serde", + "tracing", +] + +[[package]] +name = "actix-rt" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15265b6b8e2347670eb363c47fc8c75208b4a4994b27192f345fcbe707804f3e" +dependencies = [ + "futures-core", + "tokio", +] + +[[package]] +name = "actix-server" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e8613a75dd50cc45f473cee3c34d59ed677c0f7b44480ce3b8247d7dc519327" +dependencies = [ + "actix-rt", + "actix-service", + "actix-utils", + "futures-core", + "futures-util", + "mio", + "num_cpus", + "socket2", + "tokio", + "tracing", +] + +[[package]] +name = "actix-service" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b894941f818cfdc7ccc4b9e60fa7e53b5042a2e8567270f9147d5591893373a" +dependencies = [ + "futures-core", + "paste", + "pin-project-lite", +] + +[[package]] +name = "actix-session" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43da8b818ae1f11049a4d218975345fe8e56ce5a5f92c11f972abcff5ff80e87" +dependencies = [ + "actix-service", + "actix-utils", + "actix-web", + "anyhow", + "async-trait", + "derive_more", + "serde", + "serde_json", + "tracing", +] + +[[package]] +name = "actix-utils" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a1dcdff1466e3c2488e1cb5c36a71822750ad43839937f85d2f4d9f8b705d8" +dependencies = [ + "local-waker", + "pin-project-lite", +] + +[[package]] +name = "actix-web" +version = "4.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd3cb42f9566ab176e1ef0b8b3a896529062b4efc6be0123046095914c4c1c96" +dependencies = [ + "actix-codec", + "actix-http", + "actix-macros", + "actix-router", + "actix-rt", + "actix-server", + "actix-service", + "actix-utils", + "actix-web-codegen", + "ahash 0.7.6", + "bytes", + "bytestring", + "cfg-if", + "cookie", + "derive_more", + "encoding_rs", + "futures-core", + "futures-util", + "http", + "itoa", + "language-tags", + "log", + "mime", + "once_cell", + "pin-project-lite", + "regex", + "serde", + "serde_json", + "serde_urlencoded", + "smallvec", + "socket2", + "time", + "url", +] + +[[package]] +name = "actix-web-codegen" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2262160a7ae29e3415554a3f1fc04c764b1540c116aa524683208078b7a75bc9" +dependencies = [ + "actix-router", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aes" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "433cfd6710c9986c576a25ca913c39d66a6474107b406f34f91d4a8923395241" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e1366e0c69c9f927b1fa5ce2c7bf9eafc8f9268c0b9800729e8b267612447c" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "ahash" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "anyhow" +version = "1.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" + +[[package]] +name = "askama_escape" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341" + +[[package]] +name = "async-trait" +version = "0.1.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.13", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5" + +[[package]] +name = "base64" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "487f1e0fcbe47deb8b0574e646def1c903389d95241dd1bbcc6ce4a715dfc0c1" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "brotli" +version = "3.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a0b1dbcc8ae29329621f8d4f0d835787c1c38bb1401979b49d13b0b305ff68" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b6561fd3f895a11e8f72af2cb7d22e08366bebc2b6b57f7744c4bda27034744" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "bytestring" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "238e4886760d98c4f899360c834fa93e62cf7f721ac3c2da375cbdf4b8679aae" +dependencies = [ + "bytes", +] + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +dependencies = [ + "jobserver", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "cookie" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" +dependencies = [ + "aes-gcm", + "base64 0.20.0", + "hkdf", + "hmac", + "percent-encoding", + "rand", + "sha2", + "subtle", + "time", + "version_check", +] + +[[package]] +name = "cpufeatures" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "280a9f2d8b3a38871a3c8a46fb80db65e5e5ed97da80c4d08bf27fb63e35e181" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "rand_core", + "typenum", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "encoding_rs" +version = "0.8.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + +[[package]] +name = "flate2" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-core" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-sink" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" + +[[package]] +name = "futures-task" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" + +[[package]] +name = "futures-util" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "ghash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" +dependencies = [ + "opaque-debug", + "polyval", +] + +[[package]] +name = "h2" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be7b54589b581f624f566bf5d8eb2bab1db736c51528720b6bd36b96b55924d" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.6", +] + +[[package]] +name = "hashlink" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69fe1fcf8b4278d860ad0548329f892a3631fb63f82574df68275f34cdbe0ffa" +dependencies = [ + "hashbrown", +] + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "hkdf" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "http" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-range" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573" + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + +[[package]] +name = "jobserver" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +dependencies = [ + "libc", +] + +[[package]] +name = "language-tags" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" + +[[package]] +name = "libc" +version = "0.2.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" + +[[package]] +name = "libsqlite3-sys" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc22eff61b133b115c6e8c74e818c628d6d5e7a502afea6f64dee076dd94326" +dependencies = [ + "pkg-config", + "vcpkg", +] + +[[package]] +name = "local-channel" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f303ec0e94c6c54447f84f3b0ef7af769858a9c4ef56ef2a986d3dcd4c3fc9c" +dependencies = [ + "futures-core", + "futures-sink", + "futures-util", + "local-waker", +] + +[[package]] +name = "local-waker" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e34f76eb3611940e0e7d53a9aaa4e6a3151f69541a282fd0dad5571420c53ff1" + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "miniz_oxide" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys", +] + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + +[[package]] +name = "paste" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" + +[[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" + +[[package]] +name = "polyval" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef234e08c11dfcb2e56f79fd70f6f2eb7f025c0ce2333e82f4f0518ecad30c6" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d0dd4be24fcdcfeaa12a432d588dc59bbad6cad3510c67e74a2b6b2fc950564" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "regex" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "rusqlite" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "549b9d036d571d42e6e85d1c1425e2ac83491075078ca9a15be021c56b1641f2" +dependencies = [ + "bitflags 2.0.2", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "ryu" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "semver" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" + +[[package]] +name = "serde" +version = "1.0.159" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065" + +[[package]] +name = "serde_json" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744" +dependencies = [ + "itoa", + "ryu", + "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 = "sha1" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "simply-shorten" +version = "0.1.0" +dependencies = [ + "actix-files", + "actix-session", + "actix-web", + "rand", + "regex", + "rusqlite", +] + +[[package]] +name = "slab" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "socket2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "time" +version = "0.3.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" +dependencies = [ + "itoa", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" + +[[package]] +name = "time-macros" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36" +dependencies = [ + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001" +dependencies = [ + "autocfg", + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "windows-sys", +] + +[[package]] +name = "tokio-util" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "log", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" + +[[package]] +name = "unicode-ident" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "universal-hash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d3160b73c9a19f7e2939a2fdad446c57c1bbbbf4d919d3213ff1267a580d8b5" +dependencies = [ + "crypto-common", + "subtle", +] + +[[package]] +name = "url" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "zstd" +version = "0.12.3+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76eea132fb024e0e13fd9c2f5d5d595d8a967aa72382ac2f9d39fcc95afd0806" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "6.0.4+zstd.1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7afb4b54b8910cf5447638cb54bf4e8a65cbedd783af98b98c62ffe91f185543" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.7+zstd.1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94509c3ba2fe55294d752b79842c530ccfab760192521df74a081a78d2b3c7f5" +dependencies = [ + "cc", + "libc", + "pkg-config", +] diff --git a/actix/Cargo.toml b/actix/Cargo.toml new file mode 100644 index 0000000..2b06a26 --- /dev/null +++ b/actix/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "simply-shorten" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +actix-web = "4" +actix-files = "0.6.2" +rusqlite = "0.29.0" +regex = "1.7.3" +rand = "0.8.5" +actix-session = {version = "0.7.2", features = ["cookie-session"]} diff --git a/src/main/resources/public/404.html b/actix/resources/404.html similarity index 100% rename from src/main/resources/public/404.html rename to actix/resources/404.html diff --git a/src/main/resources/public/assets/favicon-196.png b/actix/resources/assets/favicon-196.png similarity index 100% rename from src/main/resources/public/assets/favicon-196.png rename to actix/resources/assets/favicon-196.png diff --git a/src/main/resources/public/assets/favicon-32.png b/actix/resources/assets/favicon-32.png similarity index 100% rename from src/main/resources/public/assets/favicon-32.png rename to actix/resources/assets/favicon-32.png diff --git a/src/main/resources/public/assets/favicon.ico b/actix/resources/assets/favicon.ico similarity index 100% rename from src/main/resources/public/assets/favicon.ico rename to actix/resources/assets/favicon.ico diff --git a/src/main/resources/public/assets/favicon.svg b/actix/resources/assets/favicon.svg similarity index 100% rename from src/main/resources/public/assets/favicon.svg rename to actix/resources/assets/favicon.svg diff --git a/src/main/resources/public/index.html b/actix/resources/index.html similarity index 100% rename from src/main/resources/public/index.html rename to actix/resources/index.html diff --git a/src/main/resources/public/js/main.js b/actix/resources/js/main.js similarity index 89% rename from src/main/resources/public/js/main.js rename to actix/resources/js/main.js index 1b9f754..354a490 100644 --- a/src/main/resources/public/js/main.js +++ b/actix/resources/js/main.js @@ -1,4 +1,4 @@ -const getSiteUrl = async () => await fetch("/api/site") +const getSiteUrl = async () => await fetch("/api/siteurl") .then(res => res.text()) .then(text => { if (text == "unset") { @@ -9,8 +9,22 @@ const getSiteUrl = async () => await fetch("/api/site") } }); +const auth_fetch = async (link) => { + let reply = await fetch(link).then(res => res.text()); + if (reply == "logged_out") { + pass = prompt("Please enter passkey to access this website"); + await fetch("/api/login", { + method: "POST", + body: pass + }); + return auth_fetch(link); + } else { + return reply; + } +} + const refreshData = async () => { - let data = await fetch("/api/all").then(res => res.text()); + let data = await auth_fetch("/api/all"); data = data .split("\n") .filter(line => line !== "") @@ -108,7 +122,7 @@ const deleteButton = (shortUrl) => { e.preventDefault(); if (confirm("Do you want to delete the entry " + shortUrl + "?")) { document.getElementById("alertBox")?.remove(); - fetch(`/api/${shortUrl}`, { + fetch(`/api/del/${shortUrl}`, { method: "DELETE" }).then(_ => refreshData()); } diff --git a/actix/src/auth.rs b/actix/src/auth.rs new file mode 100644 index 0000000..fd9b612 --- /dev/null +++ b/actix/src/auth.rs @@ -0,0 +1,47 @@ +use actix_session::Session; +use std::time::SystemTime; + +pub fn validate(session: Session) -> bool { + let token = session.get::("session-token"); + if token.is_err() { + false + } else if !check(token.unwrap()) { + false + } else { + true + } +} + +fn check(token: Option) -> bool { + if token.is_none() { + false + } else { + let token_body = token.unwrap(); + let token_parts: Vec<&str> = token_body.split(";").collect(); + if token_parts.len() < 2 { + false + } else { + let token_text = token_parts[0]; + let token_time = token_parts[1].parse::().unwrap_or(0); + let time_now = SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .expect("Time went backwards!") + .as_secs(); + if token_text == "valid-session-token" && time_now < token_time + 1209600 { + // There are 1209600 seconds in 14 days + true + } else { + false + } + } + } +} + +pub fn gen_token() -> String { + let token_text = "valid-session-token".to_string(); + let time = SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .expect("Time went backwards!") + .as_secs(); + format!("{token_text};{time}") +} diff --git a/actix/src/database.rs b/actix/src/database.rs new file mode 100644 index 0000000..3239217 --- /dev/null +++ b/actix/src/database.rs @@ -0,0 +1,73 @@ +use rusqlite::Connection; + +pub fn find_url(shortlink: &str, db: &Connection) -> String { + let mut statement = db + .prepare_cached("SELECT long_url FROM urls WHERE short_url = ?1") + .unwrap(); + + let links = statement + .query_map([shortlink], |row| Ok(row.get("long_url")?)) + .unwrap(); + + let mut longlink = "".to_string(); + for link in links { + longlink = link.unwrap(); + } + + longlink +} + +pub fn getall(db: &Connection) -> Vec { + let mut statement = db.prepare_cached("SELECT * FROM urls").unwrap(); + + let mut data = statement.query([]).unwrap(); + + let mut links: Vec = Vec::new(); + while let Some(row) = data.next().unwrap() { + let short_url: String = row.get("short_url").unwrap(); + let long_url: String = row.get("long_url").unwrap(); + let hits: i64 = row.get("hits").unwrap(); + links.push(format!("{short_url},{long_url},{hits}")); + } + + links +} + +pub fn add_hit(shortlink: &str, db: &Connection) -> () { + db.execute( + "UPDATE urls SET hits = hits + 1 WHERE short_url = ?1", + [shortlink], + ) + .unwrap(); +} + +pub fn add_link(shortlink: String, longlink: String, db: &Connection) -> bool { + match db.execute( + "INSERT INTO urls (long_url, short_url, hits) VALUES (?1, ?2, ?3)", + (longlink, shortlink, 0), + ) { + Ok(_) => true, + Err(_) => false, + } +} + +pub fn delete_link(shortlink: String, db: &Connection) -> () { + db.execute("DELETE FROM urls WHERE short_url = ?1", [shortlink]) + .unwrap(); +} + +pub fn open_db(path: String) -> Connection { + let db = Connection::open(path).expect("Unable to open database!"); + // Create table if it doesn't exist + db.execute( + "CREATE TABLE IF NOT EXISTS urls ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + long_url TEXT NOT NULL, + short_url TEXT NOT NULL, + hits INTEGER NOT NULL + )", + [], + ) + .unwrap(); + db +} diff --git a/actix/src/main.rs b/actix/src/main.rs new file mode 100644 index 0000000..30d444b --- /dev/null +++ b/actix/src/main.rs @@ -0,0 +1,140 @@ +use std::env; + +use actix_files::{Files, NamedFile}; +use actix_session::{storage::CookieSessionStore, Session, SessionMiddleware}; +use actix_web::{ + cookie::Key, + delete, get, middleware, post, + web::{self, Redirect}, + App, HttpResponse, HttpServer, Responder, +}; +use rusqlite::Connection; +mod auth; +mod database; +mod utils; + +// This struct represents state +struct AppState { + db: Connection, +} + +// Define the routes + +// Add new links +#[post("/api/new")] +async fn add_link(req: String, data: web::Data, session: Session) -> HttpResponse { + if auth::validate(session) { + let out = utils::add_link(req, &data.db); + if out.0 { + HttpResponse::Ok().body(out.1) + } else { + HttpResponse::BadRequest().body(out.1) + } + } else { + HttpResponse::Forbidden().body("logged_out") + } +} + +// Return all active links +#[get("/api/all")] +async fn getall(data: web::Data, session: Session) -> HttpResponse { + if auth::validate(session) { + HttpResponse::Ok().body(utils::getall(&data.db)) + } else { + HttpResponse::Forbidden().body("logged_out") + } +} + +// Get the site URL +#[get("/api/siteurl")] +async fn siteurl(session: Session) -> HttpResponse { + if auth::validate(session) { + let site_url = env::var("site_url").unwrap_or("unset".to_string()); + HttpResponse::Ok().body(site_url) + } else { + HttpResponse::Forbidden().body("logged_out") + } +} + +// 404 error page +#[get("/err/404")] +async fn error404() -> impl Responder { + NamedFile::open_async("./resources/404.html").await +} + +// Handle a given shortlink +#[get("/{shortlink}")] +async fn link_handler(shortlink: web::Path, data: web::Data) -> impl Responder { + let shortlink_str = shortlink.to_string(); + let longlink = utils::get_longurl(shortlink_str, &data.db); + if longlink == "".to_string() { + Redirect::to("/err/404") + } else { + database::add_hit(shortlink.as_str(), &data.db); + Redirect::to(longlink).permanent() + } +} + +// Handle login +#[post("/api/login")] +async fn login(req: String, session: Session) -> HttpResponse { + if req == env::var("password").unwrap_or(req.clone()) { + // If no password was provided, match any password + session.insert("session-token", auth::gen_token()).unwrap(); + HttpResponse::Ok().body("Correct password!") + } else { + eprintln!("Failed login attempt!"); + HttpResponse::Forbidden().body("Wrong password!") + } +} + +// Delete a given shortlink +#[delete("/api/del/{shortlink}")] +async fn delete_link( + shortlink: web::Path, + data: web::Data, + session: Session, +) -> HttpResponse { + if auth::validate(session) { + database::delete_link(shortlink.to_string(), &data.db); + HttpResponse::Ok().body("") + } else { + HttpResponse::Forbidden().body("Wrong password!") + } +} + +#[actix_web::main] +async fn main() -> std::io::Result<()> { + // Generate session key in runtime so that restarts invalidates older logins + let secret_key = Key::generate(); + let db_location = env::var("db_url").unwrap_or("/urls.sqlite".to_string()); + let port = env::var("port") + .unwrap_or("4567".to_string()) + .parse::() + .expect("Supplied port is not an integer"); + + // Actually start the server + HttpServer::new(move || { + App::new() + .wrap(SessionMiddleware::new( + CookieSessionStore::default(), + secret_key.clone(), + )) + // Maintain a single instance of database throughout + .app_data(web::Data::new(AppState { + db: database::open_db(env::var("db_url").unwrap_or(db_location.clone())), + })) + .wrap(middleware::Compress::default()) + .service(link_handler) + .service(error404) + .service(getall) + .service(siteurl) + .service(add_link) + .service(delete_link) + .service(login) + .default_service(Files::new("/", "./resources/").index_file("index.html")) + }) + .bind(("0.0.0.0", port))? + .run() + .await +} diff --git a/src/main/java/tk/SinTan1729/url/Utils.java b/actix/src/utils.rs similarity index 67% rename from src/main/java/tk/SinTan1729/url/Utils.java rename to actix/src/utils.rs index f79dc5b..4559da0 100644 --- a/src/main/java/tk/SinTan1729/url/Utils.java +++ b/actix/src/utils.rs @@ -1,18 +1,53 @@ -package tk.SinTan1729.url; +use crate::database; +use rand::seq::SliceRandom; +use regex::Regex; +use rusqlite::Connection; -import java.util.Random; -import java.util.regex.Pattern; +pub fn get_longurl(shortlink: String, db: &Connection) -> String { + if validate_link(&shortlink) { + database::find_url(shortlink.as_str(), db) + } else { + "".to_string() + } +} -public class Utils { - private static final Random random = new Random(System.currentTimeMillis()); +fn validate_link(link: &str) -> bool { + let re = Regex::new("[a-z0-9-_]+").unwrap(); + re.is_match(link) +} - private static final String SHORT_URL_PATTERN = "[a-z0-9-_]+"; - private static final Pattern PATTERN = Pattern.compile(SHORT_URL_PATTERN); - - // The following lists are modified versions of the strings in - // https://github.com/moby/moby/blob/master/pkg/namesgenerator/names-generator.go +pub fn getall(db: &Connection) -> String { + let links = database::getall(db); + links.join("\n") +} - private static final String[] adjective = new String[] {"admiring", "adoring", "affectionate", "agitated", "amazing", "angry", "awesome", "beautiful", +pub fn add_link(req: String, db: &Connection) -> (bool, String) { + let chunks: Vec<&str> = req.split(';').collect(); + let longlink = chunks[0].to_string(); + + let mut shortlink; + if chunks.len() > 1 { + shortlink = chunks[1].to_string().to_lowercase(); + if shortlink == "".to_string() { + shortlink = random_name(); + } + } else { + shortlink = random_name(); + } + + if validate_link(shortlink.as_str()) && get_longurl(shortlink.clone(), db) == "".to_string() { + ( + database::add_link(shortlink.clone(), longlink, db), + shortlink, + ) + } else { + (false, "shortUrl not valid or already in use".to_string()) + } +} + +fn random_name() -> String { + #[rustfmt::skip] + static ADJECTIVES: [&str; 108] = ["admiring", "adoring", "affectionate", "agitated", "amazing", "angry", "awesome", "beautiful", "blissful", "bold", "boring", "brave", "busy", "charming", "clever", "compassionate", "competent", "condescending", "confident", "cool", "cranky", "crazy", "dazzling", "determined", "distracted", "dreamy", "eager", "ecstatic", "elastic", "elated", "elegant", "eloquent", "epic", "exciting", "fervent", "festive", "flamboyant", "focused", "friendly", "frosty", "funny", "gallant", "gifted", "goofy", "gracious", @@ -21,9 +56,9 @@ public class Utils { "nifty", "nostalgic", "objective", "optimistic", "peaceful", "pedantic", "pensive", "practical", "priceless", "quirky", "quizzical", "recursing", "relaxed", "reverent", "romantic", "sad", "serene", "sharp", "silly", "sleepy", "stoic", "strange", "stupefied", "suspicious", "sweet", "tender", "thirsty", "trusting", "unruffled", "upbeat", "vibrant", "vigilant", "vigorous", "wizardly", "wonderful", "xenodochial", - "youthful", "zealous", "zen"}; - - private static final String[] name = new String[] {"agnesi", "albattani", "allen", "almeida", "antonelli", "archimedes", "ardinghelli", "aryabhata", "austin", + "youthful", "zealous", "zen"]; + #[rustfmt::skip] + static NAMES: [&str; 241] = ["agnesi", "albattani", "allen", "almeida", "antonelli", "archimedes", "ardinghelli", "aryabhata", "austin", "babbage", "banach", "banzai", "bardeen", "bartik", "bassi", "beaver", "bell", "benz", "bhabha", "bhaskara", "black", "blackburn", "blackwell", "bohr", "booth", "borg", "bose", "bouman", "boyd", "brahmagupta", "brattain", "brown", "buck", "burnell", "cannon", "carson", "cartwright", "carver", "cauchy", "cerf", "chandrasekhar", "chaplygin", "chatelet", "chatterjee", "chaum", "chebyshev", "clarke", "cohen", "colden", "cori", @@ -40,24 +75,11 @@ public class Utils { "rhodes", "ride", "riemann", "ritchie", "robinson", "roentgen", "rosalind", "rubin", "saha", "sammet", "sanderson", "satoshi", "shamir", "shannon", "shaw", "shirley", "shockley", "shtern", "sinoussi", "snyder", "solomon", "spence", "stonebraker", "sutherland", "swanson", "swartz", "swirles", "taussig", "tesla", "tharp", "thompson", "torvalds", "tu", "turing", "varahamihira", "vaughan", "vaughn", "villani", "visvesvaraya", "volhard", - "wescoff", "weierstrass", "wilbur", "wiles", "williams", "williamson", "wilson", "wing", "wozniak", "wright", "wu", "yalow", "yonath", "zhukovsky"}; + "wescoff", "weierstrass", "wilbur", "wiles", "williams", "williamson", "wilson", "wing", "wozniak", "wright", "wu", "yalow", "yonath", "zhukovsky"]; - public static boolean validate(String shortUrl) { - return PATTERN.matcher(shortUrl) - .matches(); - } - - public static boolean isPasswordEnabled(){ - String disablePasswordEnv = System.getenv("INSECURE_DISABLE_PASSWORD"); - - if(disablePasswordEnv != null && disablePasswordEnv.equals("I_KNOW_ITS_BAD")){ - return false; - } - - return true; - } - - public static String randomName() { - return adjective[random.nextInt(adjective.length)]+"-"+name[random.nextInt(name.length)]; - } + format!( + "{0}-{1}", + NAMES.choose(&mut rand::thread_rng()).unwrap(), + ADJECTIVES.choose(&mut rand::thread_rng()).unwrap() + ) } diff --git a/build.gradle b/build.gradle deleted file mode 100644 index 98a442d..0000000 --- a/build.gradle +++ /dev/null @@ -1,33 +0,0 @@ - -plugins { - // Apply the java plugin to add support for Java - id 'java' - - // Apply the application plugin to add support for building a CLI application. - id 'application' -} - -repositories { - jcenter() -} - -task fatJar(type: Jar) { - manifest { - attributes 'Main-Class': 'tk.SinTan1729.url.App' - } - archiveBaseName = 'url' - duplicatesStrategy = DuplicatesStrategy.EXCLUDE - from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } } - with jar -} - -dependencies { - implementation 'com.sparkjava:spark-core:2.9.4' - implementation 'com.qmetric:spark-authentication:1.4' - implementation 'org.slf4j:slf4j-simple:1.6.1' - implementation group: 'org.xerial', name: 'sqlite-jdbc', version: '3.30.1' -} - -application { - mainClassName = 'tk.SinTan1729.url.App' -} diff --git a/compose.yaml b/compose.yaml index 61f1af4..32dc91c 100644 --- a/compose.yaml +++ b/compose.yaml @@ -8,6 +8,7 @@ services: environment: # Change if you want to mount the database somewhere else # In this case, you can get rid of the db volume below + # and instead do a mount manually by specifying the location # - db_url=/urls.sqlite # Change it in case you want to set the website name # displayed in front of the shorturls, defaults to diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 249e583..0000000 Binary files a/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index f42e62f..0000000 --- a/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,5 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew deleted file mode 100755 index a69d9cb..0000000 --- a/gradlew +++ /dev/null @@ -1,240 +0,0 @@ -#!/bin/sh - -# -# Copyright © 2015-2021 the original authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -############################################################################## -# -# Gradle start up script for POSIX generated by Gradle. -# -# Important for running: -# -# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is -# noncompliant, but you have some other compliant shell such as ksh or -# bash, then to run this script, type that shell name before the whole -# command line, like: -# -# ksh Gradle -# -# Busybox and similar reduced shells will NOT work, because this script -# requires all of these POSIX shell features: -# * functions; -# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», -# «${var#prefix}», «${var%suffix}», and «$( cmd )»; -# * compound commands having a testable exit status, especially «case»; -# * various built-in commands including «command», «set», and «ulimit». -# -# Important for patching: -# -# (2) This script targets any POSIX shell, so it avoids extensions provided -# by Bash, Ksh, etc; in particular arrays are avoided. -# -# The "traditional" practice of packing multiple parameters into a -# space-separated string is a well documented source of bugs and security -# problems, so this is (mostly) avoided, by progressively accumulating -# options in "$@", and eventually passing that to Java. -# -# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, -# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; -# see the in-line comments for details. -# -# There are tweaks for specific operating systems such as AIX, CygWin, -# Darwin, MinGW, and NonStop. -# -# (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt -# within the Gradle project. -# -# You can find Gradle at https://github.com/gradle/gradle/. -# -############################################################################## - -# Attempt to set APP_HOME - -# Resolve links: $0 may be a link -app_path=$0 - -# Need this for daisy-chained symlinks. -while - APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path - [ -h "$app_path" ] -do - ls=$( ls -ld "$app_path" ) - link=${ls#*' -> '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" -APP_BASE_NAME=${0##*/} - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ - "$@" - -# Stop when "xargs" is not available. -if ! command -v xargs >/dev/null 2>&1 -then - die "xargs is not available" -fi - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat deleted file mode 100644 index f127cfd..0000000 --- a/gradlew.bat +++ /dev/null @@ -1,91 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index 5adf1f8..0000000 --- a/settings.gradle +++ /dev/null @@ -1,10 +0,0 @@ -/* - * This file was generated by the Gradle 'init' task. - * - * The settings file is used to specify which projects to include in your build. - * - * Detailed information about configuring a multi-project build in Gradle can be found - * in the user manual at https://docs.gradle.org/6.1.1/userguide/multi_project_builds.html - */ - -rootProject.name = 'url' diff --git a/src/main/java/tk/SinTan1729/url/App.java b/src/main/java/tk/SinTan1729/url/App.java deleted file mode 100644 index 1903c81..0000000 --- a/src/main/java/tk/SinTan1729/url/App.java +++ /dev/null @@ -1,44 +0,0 @@ -package tk.SinTan1729.url; - -import static spark.Spark.*; - -public class App { - - public static void main(String[] args) { - // Useful for developing the frontend - // http://sparkjava.com/documentation#examples-and-faq -> How do I enable automatic refresh of static files? - if (System.getenv("dev") != null) { - String projectDir = System.getProperty("user.dir"); - String staticDir = "/src/main/resources/public"; - staticFiles.externalLocation(projectDir + staticDir); - } else { - staticFiles.location("/public"); - } - - port(Integer.parseInt(System.getenv().getOrDefault("port", "4567"))); - - // Add GZIP compression - after(Filters::addGZIP); - - // No need to auth in dev - if (System.getenv("dev") == null && Utils.isPasswordEnabled()) { - // Authenticate - before("/api/*", Filters.createAuthFilter()); - } - - get("/", (req, res) -> { - res.redirect("/index.html"); - return "Redirect"; - }); - - - path("/api", () -> { - get("/all", Routes::getAll); - post("/new", Routes::addUrl); - delete("/:shortUrl", Routes::delete); - get("/site", Routes::getSiteUrl); - }); - - get("/:shortUrl", Routes::goToLongUrl); - } -} diff --git a/src/main/java/tk/SinTan1729/url/Filters.java b/src/main/java/tk/SinTan1729/url/Filters.java deleted file mode 100644 index d54f487..0000000 --- a/src/main/java/tk/SinTan1729/url/Filters.java +++ /dev/null @@ -1,20 +0,0 @@ -package tk.SinTan1729.url; - -import com.qmetric.spark.authentication.AuthenticationDetails; -import com.qmetric.spark.authentication.BasicAuthenticationFilter; -import spark.Filter; -import spark.Request; -import spark.Response; - -public class Filters { - public static void addGZIP(Request request, Response response) { - response.header("Content-Encoding", "gzip"); - } - - public static Filter createAuthFilter() { - String username = System.getenv("username"); - String password = System.getenv("password"); - - return new BasicAuthenticationFilter(new AuthenticationDetails(username, password)); - } -} diff --git a/src/main/java/tk/SinTan1729/url/Routes.java b/src/main/java/tk/SinTan1729/url/Routes.java deleted file mode 100644 index 42c6037..0000000 --- a/src/main/java/tk/SinTan1729/url/Routes.java +++ /dev/null @@ -1,76 +0,0 @@ -package tk.SinTan1729.url; - -import org.eclipse.jetty.http.HttpStatus; -import spark.Request; -import spark.Response; - -public class Routes { - - private static final UrlRepository urlRepository; - - static { - urlRepository = new UrlRepository(); - } - - public static String getAll(Request req, Response res) { - return String.join("\n", urlRepository.getAll()); - } - - public static String addUrl(Request req, Response res) { - var body = req.body(); - var split = body.split(";"); - String longUrl = split[0]; - boolean unique = false; - - String shortUrl; - try { - shortUrl = split[1]; - shortUrl = shortUrl.toLowerCase(); - if (urlRepository.findForShortUrl(shortUrl).isEmpty()) { - unique = true; - } - } catch (ArrayIndexOutOfBoundsException e) { - do { - shortUrl = Utils.randomName(); - if (urlRepository.findForShortUrl(shortUrl).isEmpty()) { - unique = true; - } - } while (unique == false); - } - - if (unique && Utils.validate(shortUrl)) { - return urlRepository.addUrl(longUrl, shortUrl); - } else { - res.status(HttpStatus.BAD_REQUEST_400); - return "shortUrl not valid or already in use"; - } - } - - public static String getSiteUrl(Request req, Response res) { - return System.getenv().getOrDefault("site_url", "unset"); - } - - - public static String goToLongUrl(Request req, Response res) { - String shortUrl = req.params("shortUrl"); - shortUrl = shortUrl.toLowerCase(); - var longUrlOpt = urlRepository - .findForShortUrl(shortUrl); - - if (longUrlOpt.isEmpty()) { - res.redirect("404.html"); - return ""; - } - urlRepository.addHit(shortUrl); - res.redirect(longUrlOpt.get(), HttpStatus.PERMANENT_REDIRECT_308); - - return ""; - } - - public static String delete(Request req, Response res) { - String shortUrl = req.params("shortUrl"); - - urlRepository.deleteEntry(shortUrl); - return ""; - } -} diff --git a/src/main/java/tk/SinTan1729/url/UrlRepository.java b/src/main/java/tk/SinTan1729/url/UrlRepository.java deleted file mode 100644 index 65e6da3..0000000 --- a/src/main/java/tk/SinTan1729/url/UrlRepository.java +++ /dev/null @@ -1,117 +0,0 @@ -package tk.SinTan1729.url; - -import java.sql.*; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; - - -public class UrlRepository { - private static final String INSERT_ROW_SQL = "INSERT INTO urls (long_url, short_url, hits) VALUES (?, ?, ?)"; - private static final String ADD_HIT_SQL = "UPDATE urls SET hits = hits + 1 WHERE short_url = ?"; - private static final String CREATE_TABLE_SQL = "CREATE TABLE IF NOT EXISTS urls\n" + - "(\n" + - " id INTEGER PRIMARY KEY AUTOINCREMENT,\n" + - " long_url TEXT NOT NULL,\n" + - " short_url TEXT NOT NULL,\n" + - " hits INTEGER NOT NULL\n" + - ");"; - private static final String SELECT_FOR_SHORT_SQL = "SELECT long_url FROM urls WHERE short_url = ?"; - private static final String DELETE_ROW_SQL = "DELETE FROM urls WHERE short_url = ?"; - - private final String databaseUrl; - - - public UrlRepository() { - String path = System.getenv().getOrDefault("db_url", "/urls.sqlite"); - - databaseUrl = "jdbc:sqlite:" + path; - - try (Connection conn = DriverManager.getConnection(databaseUrl)) { - if (conn != null) { - DatabaseMetaData meta = conn.getMetaData(); - - conn.createStatement() - .execute(CREATE_TABLE_SQL); - - System.out.println("Database initialised"); - } - - } catch (SQLException e) { - System.out.println(e.getMessage()); - } - } - - public List getAll() { - try (final var con = DriverManager.getConnection(databaseUrl)) { - var statement = con.createStatement(); - - statement.execute("SELECT * FROM urls"); - ResultSet rs = statement.getResultSet(); - - List result = new ArrayList<>(); - - while (rs.next()) { - result.add(String.format("%s,%s,%s", rs.getString("short_url"), rs.getString("long_url"), String.valueOf(rs.getInt("hits")))); - } - - return result; - - } catch (SQLException e) { - e.printStackTrace(); - } - return List.of(); - } - - public String addUrl(String longURL, String shortUrl) { - try (final var con = DriverManager.getConnection(databaseUrl)) { - final var stmt = con.prepareStatement(INSERT_ROW_SQL); - stmt.setString(1, longURL); - stmt.setString(2, shortUrl); - stmt.setInt(3, 0); - if (stmt.execute()) { - return String.format("%s,%s", shortUrl, longURL); - } - } catch (SQLException e) { - e.printStackTrace(); - } - return shortUrl; - } - - public void addHit(String shortURL) { - try (final var con = DriverManager.getConnection(databaseUrl)) { - final var stmt = con.prepareStatement(ADD_HIT_SQL); - stmt.setString(1, shortURL); - stmt.execute(); - } catch (SQLException e) { - e.printStackTrace(); - } - } - - public Optional findForShortUrl(String shortUrl) { - try (final var con = DriverManager.getConnection(databaseUrl)) { - final var stmt = con.prepareStatement(SELECT_FOR_SHORT_SQL); - stmt.setString(1, shortUrl); - if (stmt.execute()) { - ResultSet rs = stmt.getResultSet(); - if (rs.next()) { - return Optional.of(rs.getString("long_url")); - } - } - return Optional.empty(); - } catch (SQLException e) { - e.printStackTrace(); - } - return Optional.empty(); - } - - public void deleteEntry(String shortUrl) { - try (final var con = DriverManager.getConnection(databaseUrl)) { - final var stmt = con.prepareStatement(DELETE_ROW_SQL); - stmt.setString(1, shortUrl); - stmt.execute(); - } catch (SQLException e) { - e.printStackTrace(); - } - } -}