diff --git a/backend/.dockerignore b/backend/.dockerignore deleted file mode 100644 index 6c0158d..0000000 --- a/backend/.dockerignore +++ /dev/null @@ -1,57 +0,0 @@ -### Gradle template -.gradle -**/build/ -!src/**/build/ - -# Ignore Gradle GUI config -gradle-app.setting - -# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) -!gradle-wrapper.jar - -# Avoid ignore Gradle wrappper properties -!gradle-wrapper.properties - -# Cache of project -.gradletasknamecache - -# Eclipse Gradle plugin generated files -# Eclipse Core -.project -# JDT-specific (Eclipse Java Development Tools) -.classpath - -### Kotlin template -# Compiled class file -*.class - -# Log file -*.log - -# BlueJ files -*.ctxt - -# Mobile Tools for Java (J2ME) -.mtj.tmp/ - -# Package Files # -*.jar -*.war -*.nar -*.ear -*.zip -*.tar.gz -*.rar - -# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml -hs_err_pid* -replay_pid* - - -### Custom - -.idea/ -.kotlin/ -data/ -compose.yaml -src/main/resources/.env diff --git a/backend/.gitignore b/backend/.gitignore index 68b4f6a..292d3aa 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -1,38 +1,101 @@ -.gradle -build/ -!gradle/wrapper/gradle-wrapper.jar -!**/src/main/**/build/ -!**/src/test/**/build/ +### Rust template +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ -### STS ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache -bin/ -!**/src/main/**/bin/ -!**/src/test/**/bin/ +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock -### IntelliJ IDEA ### -.idea +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +# RustRover +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format *.iws -*.iml -*.ipr + +# IntelliJ out/ -!**/src/main/**/out/ -!**/src/test/**/out/ -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ +# mpeltonen/sbt-idea plugin +.idea_modules/ -### VS Code ### -.vscode/ +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser -**/src/main/resources/.env* diff --git a/backend/Cargo.toml b/backend/Cargo.toml new file mode 100644 index 0000000..e830f69 --- /dev/null +++ b/backend/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "antifed" +version = "0.0.1" +edition = "2024" + +[dependencies] diff --git a/backend/Dockerfile b/backend/Dockerfile deleted file mode 100644 index 6e9545e..0000000 --- a/backend/Dockerfile +++ /dev/null @@ -1,27 +0,0 @@ -# DO NOT EDIT -# source: https://ktor.io/docs/docker.html#manual - -# Stage 1: Cache Gradle dependencies -FROM gradle:jdk17 AS cache -RUN mkdir -p /home/gradle/cache_home -ENV GRADLE_USER_HOME=/home/gradle/cache_home -COPY build.gradle.* gradle.properties /home/gradle/app/ -COPY gradle /home/gradle/app/gradle -WORKDIR /home/gradle/app -RUN gradle dependencies --no-daemon - -# Stage 2: Build Application -FROM gradle:jdk17 AS build -COPY --from=cache /home/gradle/cache_home /home/gradle/.gradle -COPY --chown=gradle:gradle . /home/gradle/src -WORKDIR /home/gradle/src -# Build the fat JAR, Gradle also supports shadow -# and boot JAR by default. -RUN gradle buildFatJar --no-daemon - -# Stage 3: Create the Runtime Image -FROM amazoncorretto:17 AS runtime -EXPOSE 8080 -RUN mkdir /app -COPY --from=build /home/gradle/src/build/libs/*.jar /app/antifed.jar -ENTRYPOINT ["java","-jar","/app/antifed.jar"] diff --git a/backend/README.md b/backend/README.md deleted file mode 100644 index 302139a..0000000 --- a/backend/README.md +++ /dev/null @@ -1,51 +0,0 @@ -# backend - -This project was created using the [Ktor Project Generator](https://start.ktor.io). - -Here are some useful links to get you started: - -- [Ktor Documentation](https://ktor.io/docs/home.html) -- [Ktor GitHub page](https://github.com/ktorio/ktor) -- The [Ktor Slack chat](https://app.slack.com/client/T09229ZC6/C0A974TJ9). You'll need - to [request an invite](https://surveys.jetbrains.com/s3/kotlin-slack-sign-up) to join. - -## Features - -Here's a list of features included in this project: - -| Name | Description | -|------------------------------------------------------------------------|------------------------------------------------------------------------------------| -| [CORS](https://start.ktor.io/p/cors) | Enables Cross-Origin Resource Sharing (CORS) | -| [Routing](https://start.ktor.io/p/routing) | Provides a structured routing DSL | -| [Authentication](https://start.ktor.io/p/auth) | Provides extension point for handling the Authorization header | -| [Request Validation](https://start.ktor.io/p/request-validation) | Adds validation for incoming requests | -| [Status Pages](https://start.ktor.io/p/status-pages) | Provides exception handling for routes | -| [Call Logging](https://start.ktor.io/p/call-logging) | Logs client requests | -| [Content Negotiation](https://start.ktor.io/p/content-negotiation) | Provides automatic content conversion according to Content-Type and Accept headers | -| [kotlinx.serialization](https://start.ktor.io/p/kotlinx-serialization) | Handles JSON serialization using kotlinx.serialization library | -| [Exposed](https://start.ktor.io/p/exposed) | Adds Exposed database to your application | -| [Rate Limiting](https://start.ktor.io/p/ktor-server-rate-limiting) | Manage request rate limiting as you see fit | -| [Compression](https://start.ktor.io/p/compression) | Compresses responses using encoding algorithms like GZIP | -| [Authentication Basic](https://start.ktor.io/p/auth-basic) | Handles 'Basic' username / password authentication scheme | - -## Building & Running - -To build or run the project, use one of the following tasks: - -| Task | Description | -|-------------------------------|----------------------------------------------------------------------| -| `./gradlew test` | Run the tests | -| `./gradlew build` | Build everything | -| `buildFatJar` | Build an executable JAR of the server with all dependencies included | -| `buildImage` | Build the docker image to use with the fat JAR | -| `publishImageToLocalRegistry` | Publish the docker image locally | -| `run` | Run the server | -| `runDocker` | Run using the local docker image | - -If the server starts successfully, you'll see the following output: - -``` -2024-12-04 14:32:45.584 [main] INFO Application - Application started in 0.303 seconds. -2024-12-04 14:32:45.682 [main] INFO Application - Responding at http://0.0.0.0:8080 -``` - diff --git a/backend/build.gradle.kts b/backend/build.gradle.kts deleted file mode 100644 index 0a92dc0..0000000 --- a/backend/build.gradle.kts +++ /dev/null @@ -1,44 +0,0 @@ -val exposed_version: String by project -val kotlin_version: String by project -val logback_version: String by project - -plugins { - kotlin("jvm") version "2.1.10" - id("io.ktor.plugin") version "3.1.3" - id("org.jetbrains.kotlin.plugin.serialization") version "2.1.10" -} - -group = "dev.svitan" -version = "1.0.0" - -application { - mainClass = "dev.svitan.ApplicationKt" -} - -repositories { - mavenCentral() - maven { url = uri("https://packages.confluent.io/maven/") } -} - -dependencies { - implementation("io.ktor:ktor-server-cors") - implementation("io.ktor:ktor-server-core") - implementation("io.ktor:ktor-server-auth") - implementation("io.ktor:ktor-server-request-validation") - implementation("io.ktor:ktor-server-host-common") - implementation("io.ktor:ktor-server-status-pages") - implementation("io.ktor:ktor-server-call-logging") - implementation("io.ktor:ktor-server-content-negotiation") - implementation("io.ktor:ktor-serialization-kotlinx-json") - implementation("org.jetbrains.exposed:exposed-core:$exposed_version") - implementation("org.jetbrains.exposed:exposed-jdbc:$exposed_version") - implementation("org.postgresql:postgresql:42.7.2") - implementation("io.github.flaxoos:ktor-server-rate-limiting:2.2.1") - implementation("io.ktor:ktor-server-compression") - implementation("io.ktor:ktor-server-netty") - implementation("ch.qos.logback:logback-classic:$logback_version") - implementation("io.github.oshai:kotlin-logging-jvm:7.0.3") - implementation("io.github.cdimascio:dotenv-kotlin:6.5.1") - testImplementation("io.ktor:ktor-server-test-host") - testImplementation("org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version") -} diff --git a/backend/compose.yaml b/backend/compose.yaml deleted file mode 100644 index d89d947..0000000 --- a/backend/compose.yaml +++ /dev/null @@ -1,18 +0,0 @@ -services: - postgres: - image: postgres:17 - restart: on-failure - - environment: - POSTGRES_USER: "user" - POSTGRES_PASSWORD: "password" - POSTGRES_DB: "db" - - volumes: - - pgdata:/var/lib/postgresql/data - - ports: - - "5432:5432" - -volumes: - pgdata: diff --git a/backend/gradle.properties b/backend/gradle.properties deleted file mode 100644 index 18193bc..0000000 --- a/backend/gradle.properties +++ /dev/null @@ -1,6 +0,0 @@ -kotlin.code.style=official -exposed_version=0.61.0 -h2_version=2.3.232 -kotlin_version=2.1.10 -ktor_version=3.4.2 -logback_version=1.5.32 diff --git a/backend/gradle/wrapper/gradle-wrapper.jar b/backend/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 7454180..0000000 Binary files a/backend/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/backend/gradle/wrapper/gradle-wrapper.properties b/backend/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index e411586..0000000 --- a/backend/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-8.4-bin.zip -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/backend/gradlew b/backend/gradlew deleted file mode 100755 index 1b6c787..0000000 --- a/backend/gradlew +++ /dev/null @@ -1,234 +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 \ - "$@" - -# 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/backend/gradlew.bat b/backend/gradlew.bat deleted file mode 100644 index 107acd3..0000000 --- a/backend/gradlew.bat +++ /dev/null @@ -1,89 +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%" == "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%"=="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! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/backend/settings.gradle.kts b/backend/settings.gradle.kts deleted file mode 100644 index 2401f0a..0000000 --- a/backend/settings.gradle.kts +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = "backend" diff --git a/backend/src/main.rs b/backend/src/main.rs new file mode 100644 index 0000000..e7a11a9 --- /dev/null +++ b/backend/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} diff --git a/backend/src/main/kotlin/Application.kt b/backend/src/main/kotlin/Application.kt deleted file mode 100644 index 5d5b0dc..0000000 --- a/backend/src/main/kotlin/Application.kt +++ /dev/null @@ -1,38 +0,0 @@ -package dev.svitan - -import io.github.cdimascio.dotenv.dotenv -import io.ktor.server.application.* -import io.ktor.server.engine.* -import io.ktor.server.netty.* -import dev.svitan.plugins.configureAdministration -import dev.svitan.plugins.configureDatabases -import dev.svitan.plugins.configureErrors -import dev.svitan.plugins.configureHTTP -import dev.svitan.plugins.configureMonitoring -import dev.svitan.plugins.configureRouting -import dev.svitan.plugins.configureSecurity -import dev.svitan.plugins.configureSerialization - -fun main() { - embeddedServer( - Netty, - port = 8080, - host = "0.0.0.0", - module = Application::module - ).start(wait = true) -} - -fun Application.module() { - val dotenv = dotenv { - ignoreIfMissing = true - } - - configureMonitoring() - configureHTTP() - configureErrors() - configureSecurity(dotenv) - configureDatabases(dotenv) - configureSerialization() - configureAdministration() - configureRouting() -} diff --git a/backend/src/main/kotlin/plugins/Administration.kt b/backend/src/main/kotlin/plugins/Administration.kt deleted file mode 100644 index fff8a6d..0000000 --- a/backend/src/main/kotlin/plugins/Administration.kt +++ /dev/null @@ -1,16 +0,0 @@ -package dev.svitan.plugins - -import io.github.flaxoos.ktor.server.plugins.ratelimiter.RateLimiting -import io.github.flaxoos.ktor.server.plugins.ratelimiter.implementations.TokenBucket -import io.ktor.server.application.* -import kotlin.time.Duration.Companion.seconds - -fun Application.configureAdministration() { - install(RateLimiting) { - rateLimiter { - type = TokenBucket::class - capacity = 100 - rate = 10.seconds - } - } -} diff --git a/backend/src/main/kotlin/plugins/Databases.kt b/backend/src/main/kotlin/plugins/Databases.kt deleted file mode 100644 index 23e727c..0000000 --- a/backend/src/main/kotlin/plugins/Databases.kt +++ /dev/null @@ -1,27 +0,0 @@ -package dev.svitan.plugins - -import dev.svitan.services.ExecutorService -import dev.svitan.services.ActionService -import dev.svitan.services.AuthService -import io.github.cdimascio.dotenv.Dotenv -import io.ktor.server.application.* -import org.jetbrains.exposed.sql.Database - -fun Application.configureDatabases(dotenv: Dotenv) { - val dbHost = dotenv["DB_HOST"] ?: throw Exception("DB_HOST not found") - val dbPort = dotenv["DB_PORT"] ?: throw Exception("DB_PORT not found") - val dbName = dotenv["DB_NAME"] ?: throw Exception("DB_NAME not found") - val dbUser = dotenv["DB_USER"] ?: throw Exception("DB_USER not found") - val dbPassword = dotenv["DB_PASSWORD"] ?: throw Exception("DB_PASSWORD not found") - - Database.connect( - url = "jdbc:postgresql://${dbHost}:${dbPort}/${dbName}", - driver = "org.postgresql.Driver", - user = dbUser, - password = dbPassword - ) - - AuthService.init() - ActionService.init() - ExecutorService.init(dotenv) -} diff --git a/backend/src/main/kotlin/plugins/Errors.kt b/backend/src/main/kotlin/plugins/Errors.kt deleted file mode 100644 index e1d1795..0000000 --- a/backend/src/main/kotlin/plugins/Errors.kt +++ /dev/null @@ -1,53 +0,0 @@ -package dev.svitan.plugins - -import io.github.oshai.kotlinlogging.KotlinLogging -import io.ktor.http.* -import io.ktor.server.application.* -import io.ktor.server.plugins.* -import io.ktor.server.plugins.statuspages.* -import io.ktor.server.request.* -import io.ktor.server.response.* - -class AuthorizationException(message: String) : Exception(message) - -fun Application.configureErrors() { - val logger = KotlinLogging.logger {} - - install(StatusPages) { - exception { call, cause -> - logger.error(cause) { "${call.request.httpMethod} ${call.request.uri}" } - - when (cause) { - is IllegalArgumentException -> call.respond( - status = HttpStatusCode.BadRequest, - message = cause.message!! - ) - - is NotFoundException -> call.respond( - status = HttpStatusCode.NotFound, - message = cause.message ?: "" - ) - - is AuthorizationException -> call.respond( - status = HttpStatusCode.Unauthorized, - message = cause.message ?: "" - ) - - is BadRequestException -> call.respond( - status = HttpStatusCode.BadRequest, - message = cause.message ?: "" - ) - - is InvalidBodyException -> call.respond( - status = HttpStatusCode.BadRequest, - message = cause.message ?: "" - ) - - else -> call.respond( - status = HttpStatusCode.InternalServerError, - message = cause.message ?: "" - ) - } - } - } -} diff --git a/backend/src/main/kotlin/plugins/HTTP.kt b/backend/src/main/kotlin/plugins/HTTP.kt deleted file mode 100644 index e09be68..0000000 --- a/backend/src/main/kotlin/plugins/HTTP.kt +++ /dev/null @@ -1,19 +0,0 @@ -package dev.svitan.plugins - -import io.ktor.http.* -import io.ktor.server.application.* -import io.ktor.server.plugins.compression.* -import io.ktor.server.plugins.cors.routing.* - -fun Application.configureHTTP() { - install(CORS) { - allowMethod(HttpMethod.Options) - allowMethod(HttpMethod.Post) - allowMethod(HttpMethod.Put) - allowMethod(HttpMethod.Patch) - allowMethod(HttpMethod.Delete) - allowHeader(HttpHeaders.Authorization) - anyHost() // @TODO: Don't do this in production if possible. Try to limit it. - } - install(Compression) -} diff --git a/backend/src/main/kotlin/plugins/Monitoring.kt b/backend/src/main/kotlin/plugins/Monitoring.kt deleted file mode 100644 index 7ac46d8..0000000 --- a/backend/src/main/kotlin/plugins/Monitoring.kt +++ /dev/null @@ -1,13 +0,0 @@ -package dev.svitan.plugins - -import io.ktor.server.application.* -import io.ktor.server.plugins.calllogging.* -import io.ktor.server.request.* -import org.slf4j.event.Level - -fun Application.configureMonitoring() { - install(CallLogging) { - level = Level.INFO - filter { call -> call.request.path().startsWith("/") } - } -} diff --git a/backend/src/main/kotlin/plugins/Routing.kt b/backend/src/main/kotlin/plugins/Routing.kt deleted file mode 100644 index e0da31d..0000000 --- a/backend/src/main/kotlin/plugins/Routing.kt +++ /dev/null @@ -1,31 +0,0 @@ -package dev.svitan.plugins - -import dev.svitan.routes.routeAction -import dev.svitan.routes.routeAuth -import io.ktor.server.application.* -import io.ktor.server.plugins.requestvalidation.* -import io.ktor.server.response.* -import io.ktor.server.routing.* - -fun Application.configureRouting() { - install(RequestValidation) { - validate { bodyText -> - if (!bodyText.startsWith("Hello")) - ValidationResult.Invalid("Body text should start with 'Hello'") - else ValidationResult.Valid - } - } - - routing { - get("/") { - call.respond("Hello World!") - } - - get("/health") { - call.respond("OK") - } - } - - routeAuth() - routeAction() -} diff --git a/backend/src/main/kotlin/plugins/Security.kt b/backend/src/main/kotlin/plugins/Security.kt deleted file mode 100644 index ea37f02..0000000 --- a/backend/src/main/kotlin/plugins/Security.kt +++ /dev/null @@ -1,22 +0,0 @@ -package dev.svitan.plugins - -import io.github.cdimascio.dotenv.Dotenv -import io.ktor.server.application.* -import io.ktor.server.auth.* - -fun Application.configureSecurity(dotenv: Dotenv) { - val apiKey = dotenv["API_KEY"] ?: throw Exception("API_KEY not found") - - install(Authentication) { - bearer { - realm = "/" - authenticate { credential -> - if (credential.token == apiKey) { - UserIdPrincipal("admin") - } else { - null - } - } - } - } -} diff --git a/backend/src/main/kotlin/plugins/Serialization.kt b/backend/src/main/kotlin/plugins/Serialization.kt deleted file mode 100644 index e74998d..0000000 --- a/backend/src/main/kotlin/plugins/Serialization.kt +++ /dev/null @@ -1,12 +0,0 @@ -package dev.svitan.plugins - -import io.ktor.serialization.kotlinx.json.json -import io.ktor.server.application.Application -import io.ktor.server.application.install -import io.ktor.server.plugins.contentnegotiation.ContentNegotiation - -fun Application.configureSerialization() { - install(ContentNegotiation) { - json() - } -} diff --git a/backend/src/main/kotlin/routes/Action.kt b/backend/src/main/kotlin/routes/Action.kt deleted file mode 100644 index 95ce474..0000000 --- a/backend/src/main/kotlin/routes/Action.kt +++ /dev/null @@ -1,75 +0,0 @@ -package dev.svitan.routes - -import dev.svitan.plugins.AuthorizationException -import dev.svitan.services.ActionKind -import dev.svitan.services.ActionService -import dev.svitan.services.AuthService -import dev.svitan.services.ExecutorService -import dev.svitan.services.NewActionDTO -import dev.svitan.services.RunActionDTO -import dev.svitan.services.UpdateActionDTO -import io.ktor.http.* -import io.ktor.server.application.* -import io.ktor.server.auth.* -import io.ktor.server.plugins.* -import io.ktor.server.request.* -import io.ktor.server.response.* -import io.ktor.server.routing.* -import java.util.* - -fun Application.routeAction() { - routing { - authentication { - get("/action") { - call.respond(ActionService.readAll()) - } - - put("/action") { - val action = call.receive() - call.respond(HttpStatusCode.Created, ActionService.create(action)) - } - - get("/action/{id}") { - val idRaw = call.parameters["id"] ?: throw BadRequestException("Invalid id") - val id = UUID.fromString(idRaw) - val action = ActionService.read(id) ?: throw NotFoundException() - call.respond(action) - } - - post("/action/{id}") { - val idRaw = call.parameters["id"] ?: throw BadRequestException("Invalid id") - val id = UUID.fromString(idRaw) - - val action = ActionService.read(id) ?: throw NotFoundException() - val run = call.receive() - AuthService.validate( - UUID.fromString(action.authId), - run.pin, - run.key - ) ?: throw AuthorizationException("Invalid pin or key") - - if (action.kind == ActionKind.TEXT) { - ExecutorService.executeSource(id, action.source) - } else { - ExecutorService.executeFile(id) - } - - call.respond(HttpStatusCode.NoContent) - } - - patch("/action/{id}") { - val idRaw = call.parameters["id"] ?: throw BadRequestException("Invalid id") - val id = UUID.fromString(idRaw) - val action = call.receive() - call.respond(ActionService.update(id, action)) - } - - delete("/action/{id}") { - val idRaw = call.parameters["id"] ?: throw BadRequestException("Invalid id") - val id = UUID.fromString(idRaw) - ActionService.delete(id) - call.respond(HttpStatusCode.NoContent) - } - } - } -} diff --git a/backend/src/main/kotlin/routes/Auth.kt b/backend/src/main/kotlin/routes/Auth.kt deleted file mode 100644 index d02a8c1..0000000 --- a/backend/src/main/kotlin/routes/Auth.kt +++ /dev/null @@ -1,41 +0,0 @@ -package dev.svitan.routes - -import dev.svitan.services.AuthService -import dev.svitan.services.NewAuthDTO -import io.ktor.http.* -import io.ktor.server.application.* -import io.ktor.server.auth.* -import io.ktor.server.plugins.* -import io.ktor.server.request.* -import io.ktor.server.response.* -import io.ktor.server.routing.* -import java.util.* - -fun Application.routeAuth() { - routing { - authenticate { - get("/auth") { - call.respond(AuthService.readAll()) - } - - put("/auth") { - val auth = call.receive() - call.respond(HttpStatusCode.Created, AuthService.create(auth)) - } - - get("/auth/{id}") { - val idRaw = call.parameters["id"] ?: throw BadRequestException("Invalid id") - val id = UUID.fromString(idRaw) - val auth = AuthService.read(id) ?: throw NotFoundException() - call.respond(auth) - } - - delete("/auth/{id}") { - val idRaw = call.parameters["id"] ?: throw BadRequestException("Invalid id") - val id = UUID.fromString(idRaw) - AuthService.delete(id) - call.respond(HttpStatusCode.NoContent) - } - } - } -} diff --git a/backend/src/main/kotlin/services/Action.kt b/backend/src/main/kotlin/services/Action.kt deleted file mode 100644 index f1eb835..0000000 --- a/backend/src/main/kotlin/services/Action.kt +++ /dev/null @@ -1,165 +0,0 @@ -package dev.svitan.services - -import io.ktor.server.plugins.NotFoundException -import kotlinx.serialization.Serializable -import org.jetbrains.exposed.sql.* -import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq -import org.jetbrains.exposed.sql.transactions.transaction -import java.time.LocalDateTime -import java.time.format.DateTimeFormatter -import java.util.UUID - -enum class ActionKind { - TEXT, SCRIPT; - - companion object { - fun from(it: String): ActionKind { - return when (it.lowercase()) { - "text" -> TEXT - "script" -> SCRIPT - else -> throw IllegalArgumentException("Invalid action kind: $it") - } - } - } - - override fun toString(): String { - return super.toString().lowercase() - } -} - -@Serializable -data class ActionDTO( - val id: String, - val name: String, - val kind: ActionKind, - val source: String, - val authId: String, - val createdAt: String, - val updatedAt: String -) - -@Serializable -data class NewActionDTO( - val name: String, - val kind: String, - val source: String, - val authId: String -) - -@Serializable -data class UpdateActionDTO( - val name: String?, - val kind: String?, - val source: String? -) - -@Serializable -data class RunActionDTO( - val pin: String, - val key: String -) - -class ActionService { - object Actions : Table("actions") { - val id = uuid("id").autoGenerate() - val name = text("name").uniqueIndex() - val kind = text("kind") - val aSource = text("source") // `source` is another property - val auth = uuid("auth_id").references(AuthService.Auths.id) - val createdAt = text("created_at") - val updatedAt = text("updated_at") - - override val primaryKey = PrimaryKey(id) - } - - companion object { - fun init() { - transaction { - SchemaUtils.create(Actions) - } - } - - fun create(action: NewActionDTO): UUID { - val id = transaction { - val now = LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) - val authId = UUID.fromString(action.authId) - AuthService.read(authId) ?: throw NotFoundException("auth not found") - - Actions.insert { - it[name] = action.name - it[kind] = ActionKind.from(action.kind).toString() - it[aSource] = if (action.kind == ActionKind.SCRIPT.toString()) "" else action.source - it[auth] = authId - it[createdAt] = now - it[updatedAt] = now - }[Actions.id] - } - - if (action.kind == ActionKind.SCRIPT.toString()) { - ExecutorService.writeFile(id, action.source) - } - return id - } - - fun read(id: UUID): ActionDTO? = transaction { - Actions.selectAll() - .where { Actions.id eq id } - .map { - ActionDTO( - it[Actions.id].toString(), - it[Actions.name], - ActionKind.from(it[Actions.kind]), - it[Actions.aSource], - it[Actions.auth].toString(), - it[Actions.createdAt], - it[Actions.updatedAt] - ) - }.singleOrNull() - } - - fun readAll(): List = transaction { - Actions.selectAll().map { - ActionDTO( - it[Actions.id].toString(), - it[Actions.name], - ActionKind.from(it[Actions.kind]), - it[Actions.aSource], - it[Actions.auth].toString(), - it[Actions.createdAt], - it[Actions.updatedAt] - ) - } - } - - fun update(id: UUID, action: UpdateActionDTO) { - val oldAction = read(id) ?: throw NotFoundException("action not found") - - transaction { - val now = LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) - Actions.update({ Actions.id eq id }) { - if (action.name != null) it[name] = action.name - if (action.kind != null) it[kind] = ActionKind.from(action.kind).toString() - if (action.source != null) it[aSource] = action.source - it[updatedAt] = now - } - } - - if (action.kind == ActionKind.SCRIPT.toString() && action.source != null) { - ExecutorService.writeFile(id, action.source) - } else if (action.kind == ActionKind.TEXT.toString() && oldAction.kind == ActionKind.SCRIPT) { - ExecutorService.deleteFile(id) - } - } - - fun delete(id: UUID) { - val action = read(id) ?: throw NotFoundException("action not found") - transaction { - Actions.deleteWhere { Actions.id eq id } - } - - if (action.kind == ActionKind.SCRIPT) { - ExecutorService.deleteFile(id) - } - } - } -} diff --git a/backend/src/main/kotlin/services/Auth.kt b/backend/src/main/kotlin/services/Auth.kt deleted file mode 100644 index 793a31d..0000000 --- a/backend/src/main/kotlin/services/Auth.kt +++ /dev/null @@ -1,102 +0,0 @@ -package dev.svitan.services - -import kotlinx.serialization.Serializable -import org.jetbrains.exposed.sql.* -import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq -import org.jetbrains.exposed.sql.transactions.transaction -import java.security.MessageDigest -import java.time.LocalDateTime -import java.time.format.DateTimeFormatter -import java.util.UUID - -@Serializable -data class AuthDTO( - val id: String, - val name: String, - val createdAt: String -) - -@Serializable -data class NewAuthDTO( - val name: String, - val pin: String, - val key: String -) - -class AuthService { - object Auths : Table("auths") { - val id = uuid("id").autoGenerate() - val name = text("name").uniqueIndex() - val pin = text("pin") - val key = text("key") - val createdAt = text("created_at") - - override val primaryKey = PrimaryKey(id) - } - - companion object { - fun init() { - transaction { - SchemaUtils.create(Auths) - } - } - - fun create(action: NewAuthDTO): UUID = transaction { - val now = LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) - Auths.insert { - it[name] = action.name - it[pin] = sha256(action.pin) - it[key] = sha256(action.key) - it[createdAt] = now - }[Auths.id] - } - - fun read(id: UUID): AuthDTO? = transaction { - Auths.selectAll() - .where { Auths.id eq id } - .map { - AuthDTO( - it[Auths.id].toString(), - it[Auths.name], - it[Auths.createdAt], - ) - }.singleOrNull() - } - - fun readAll(): List = transaction { - Auths.selectAll().map { - AuthDTO( - it[Auths.id].toString(), - it[Auths.name], - it[Auths.createdAt], - ) - } - } - - fun validate(id: UUID, pin: String, key: String): AuthDTO? = transaction { - Auths.selectAll() - .where { (Auths.id eq id) and (Auths.pin eq sha256(pin)) and (Auths.key eq sha256(key)) } - .map { - AuthDTO( - it[Auths.id].toString(), - it[Auths.name], - it[Auths.createdAt] - ) - } - .singleOrNull() - } - - fun delete(id: UUID) { - transaction { - Auths.deleteWhere { Auths.id eq id } - } - } - } -} - -fun sha256(it: String): String { - val bytes = it.toByteArray() - val md = MessageDigest.getInstance("SHA-256") - val digest = md.digest(bytes) - return digest.joinToString("") { "%02x".format(it) } -} diff --git a/backend/src/main/kotlin/services/Executor.kt b/backend/src/main/kotlin/services/Executor.kt deleted file mode 100644 index d854161..0000000 --- a/backend/src/main/kotlin/services/Executor.kt +++ /dev/null @@ -1,71 +0,0 @@ -package dev.svitan.services - -import io.github.cdimascio.dotenv.Dotenv -import java.io.File -import java.time.LocalDateTime -import java.time.format.DateTimeFormatter -import java.util.UUID - -class ExecutorService { - companion object { - var dir: File? = null - - fun init(dotenv: Dotenv) { - val path = dotenv["DATA_DIR"] ?: throw Exception("DATA_DIR not found") - dir = File(path) - - if (!dir!!.exists()) { - val created = dir!!.mkdirs() - if (!created) throw Exception("Unable to create data directory") - } else if (dir!!.isFile) { - throw Exception("DATA_DIR must be a directory") - } else if (!dir!!.canRead() || !dir!!.canWrite() || !dir!!.canExecute()) { - throw Exception("DATA_DIR must be readable, writable, and executable") - } - } - - fun writeFile(uuid: UUID, content: String) { - if (dir == null) throw Exception("FilesystemService not initialized") - - val file = File(dir, "$uuid.sh") - file.writeText(content) - } - - fun deleteFile(uuid: UUID) { - if (dir == null) throw Exception("FilesystemService not initialized") - - val file = File(dir, "$uuid.sh") - if (!file.exists()) throw Exception("File not found") - - val deleted = file.delete() - if (!deleted) throw Exception("Unable to delete file") - } - - fun executeFile(uuid: UUID) { - if (dir == null) throw Exception("FilesystemService not initialized") - - val file = File(dir, "$uuid.sh") - if (!file.exists() || !file.canExecute()) throw Exception("File not found or not executable") - - val now = LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) - val stdout = File(dir, "$uuid-$now-stdout") - val stderr = File(dir, "$uuid-$now-stderr") - ProcessBuilder("bash", file.absolutePath) - .redirectOutput(ProcessBuilder.Redirect.to(stdout)) - .redirectError(ProcessBuilder.Redirect.to(stderr)) - .start() - } - - fun executeSource(uuid: UUID, source: String) { - if (dir == null) throw Exception("FilesystemService not initialized") - - val now = LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) - val stdout = File(dir, "$uuid-$now-stdout") - val stderr = File(dir, "$uuid-$now-stderr") - ProcessBuilder("bash", "-c", source) - .redirectOutput(ProcessBuilder.Redirect.to(stdout)) - .redirectError(ProcessBuilder.Redirect.to(stderr)) - .start() - } - } -} diff --git a/backend/src/main/resources/logback.xml b/backend/src/main/resources/logback.xml deleted file mode 100644 index 7b7cb82..0000000 --- a/backend/src/main/resources/logback.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - %d{YYYY-MM-dd HH:mm:ss.SSS} %-5level %logger{36} - %msg%n - - - - - - - - diff --git a/backend/src/test/kotlin/ApplicationTest.kt b/backend/src/test/kotlin/ApplicationTest.kt deleted file mode 100644 index fe8bf2b..0000000 --- a/backend/src/test/kotlin/ApplicationTest.kt +++ /dev/null @@ -1,20 +0,0 @@ -package dev.svitan - -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.server.testing.* -import kotlin.test.Test -import kotlin.test.assertEquals - -class ApplicationTest { - @Test - fun testRoot() = testApplication { - application { - module() - } - - client.get("/").apply { - assertEquals(HttpStatusCode.OK, status) - } - } -} diff --git a/frontend/.gitignore b/frontend/.gitignore deleted file mode 100644 index aa724b7..0000000 --- a/frontend/.gitignore +++ /dev/null @@ -1,15 +0,0 @@ -*.iml -.gradle -/local.properties -/.idea/caches -/.idea/libraries -/.idea/modules.xml -/.idea/workspace.xml -/.idea/navEditor.xml -/.idea/assetWizardSettings.xml -.DS_Store -/build -/captures -.externalNativeBuild -.cxx -local.properties diff --git a/frontend/app/.gitignore b/frontend/app/.gitignore deleted file mode 100644 index 42afabf..0000000 --- a/frontend/app/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build \ No newline at end of file diff --git a/frontend/app/build.gradle.kts b/frontend/app/build.gradle.kts deleted file mode 100644 index d3e1b0b..0000000 --- a/frontend/app/build.gradle.kts +++ /dev/null @@ -1,67 +0,0 @@ -plugins { - alias(libs.plugins.android.application) - alias(libs.plugins.kotlin.android) - alias(libs.plugins.kotlin.compose) - alias(libs.plugins.kotlin.serialization) -} - -android { - namespace = "dev.svitan.antifed" - compileSdk = 37 - - defaultConfig { - applicationId = "dev.svitan.antifed" - minSdk = 34 - targetSdk = 35 - versionCode = 1 - versionName = "1.0" - - testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - } - - buildTypes { - release { - isMinifyEnabled = false - proguardFiles( - getDefaultProguardFile("proguard-android-optimize.txt"), - "proguard-rules.pro" - ) - } - } - compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 - } - kotlinOptions { - jvmTarget = "11" - } - buildFeatures { - compose = true - } -} - -dependencies { - implementation(libs.androidx.core.ktx) - implementation(libs.androidx.lifecycle.runtime.ktx) - implementation(libs.androidx.activity.compose) - implementation(platform(libs.androidx.compose.bom)) - implementation(libs.androidx.ui) - implementation(libs.androidx.ui.graphics) - implementation(libs.androidx.ui.tooling.preview) - implementation(libs.androidx.material3) - implementation(libs.androidx.compose.material.icons.extended) - implementation(libs.androidx.appcompat) - implementation(libs.material) - implementation(libs.androidx.activity) - implementation(libs.ktor.client.core) - implementation(libs.ktor.client.content.negotiation) - implementation(libs.ktor.client.cio) - implementation(libs.ktor.serialization.kotlinx.json) - testImplementation(libs.junit) - androidTestImplementation(libs.androidx.junit) - androidTestImplementation(libs.androidx.espresso.core) - androidTestImplementation(platform(libs.androidx.compose.bom)) - androidTestImplementation(libs.androidx.ui.test.junit4) - debugImplementation(libs.androidx.ui.tooling) - debugImplementation(libs.androidx.ui.test.manifest) -} diff --git a/frontend/app/proguard-rules.pro b/frontend/app/proguard-rules.pro deleted file mode 100644 index 481bb43..0000000 --- a/frontend/app/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/frontend/app/src/androidTest/java/dev/svitan/antifed/ExampleInstrumentedTest.kt b/frontend/app/src/androidTest/java/dev/svitan/antifed/ExampleInstrumentedTest.kt deleted file mode 100644 index f404580..0000000 --- a/frontend/app/src/androidTest/java/dev/svitan/antifed/ExampleInstrumentedTest.kt +++ /dev/null @@ -1,24 +0,0 @@ -package dev.svitan.antifed - -import androidx.test.platform.app.InstrumentationRegistry -import androidx.test.ext.junit.runners.AndroidJUnit4 - -import org.junit.Test -import org.junit.runner.RunWith - -import org.junit.Assert.* - -/** - * Instrumented test, which will execute on an Android device. - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -@RunWith(AndroidJUnit4::class) -class ExampleInstrumentedTest { - @Test - fun useAppContext() { - // Context of the app under test. - val appContext = InstrumentationRegistry.getInstrumentation().targetContext - assertEquals("dev.svitan.antifed", appContext.packageName) - } -} \ No newline at end of file diff --git a/frontend/app/src/main/AndroidManifest.xml b/frontend/app/src/main/AndroidManifest.xml deleted file mode 100644 index c6e2329..0000000 --- a/frontend/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/frontend/app/src/main/ic_launcher-playstore.png b/frontend/app/src/main/ic_launcher-playstore.png deleted file mode 100644 index 8e59e6f..0000000 Binary files a/frontend/app/src/main/ic_launcher-playstore.png and /dev/null differ diff --git a/frontend/app/src/main/java/dev/svitan/antifed/API.kt b/frontend/app/src/main/java/dev/svitan/antifed/API.kt deleted file mode 100644 index 1f4d53d..0000000 --- a/frontend/app/src/main/java/dev/svitan/antifed/API.kt +++ /dev/null @@ -1,98 +0,0 @@ -package dev.svitan.antifed - -import android.content.Context -import android.content.SharedPreferences -import io.ktor.client.HttpClient -import io.ktor.client.call.body -import io.ktor.client.engine.cio.CIO -import io.ktor.client.plugins.contentnegotiation.ContentNegotiation -import io.ktor.client.request.get -import io.ktor.client.request.headers -import io.ktor.http.HttpStatusCode -import io.ktor.serialization.kotlinx.json.json -import kotlinx.serialization.Serializable - -class API { - companion object { - val client = HttpClient(CIO) { - install(ContentNegotiation) { - json() - } - } - - val apiDataHolder = APIDataHolder.getInstance() - val authDataHolder = AuthDataHolder.getInstance() - - suspend fun doesServerRespond(): Boolean { - // route '/' should return 'Hello world' - val response = client.get(apiDataHolder.serverUrl) - return response.status == HttpStatusCode.OK - } - - suspend fun loadAuths() { - val response = client.get("${apiDataHolder.serverUrl}/auth") { - headers { - append("Authorization", "Bearer ${apiDataHolder.token}") - } - } - - if (response.status == HttpStatusCode.OK) { - authDataHolder.auths = response.body>() - authDataHolder.authIndex = - authDataHolder.auths.indexOfFirst { it.id == authDataHolder.authId } - } else { - throw Exception("Error loading auths") - } - } - } -} - -class APIDataHolder { - var serverUrl: String = "" - var token: String = "" - var isOk: Boolean = false - - private var initialized: Boolean = false - - fun initializeFromPrefs(context: Context, prefs: SharedPreferences) { - if (initialized) return; - - serverUrl = prefs.getString(context.getString(R.string.server_url_key), "") ?: "" - token = prefs.getString(context.getString(R.string.token_key), "") ?: "" - - val isServerUrlOk = prefs.getBoolean(context.getString(R.string.server_url_okay_key), false) - val isTokenOk = prefs.getBoolean(context.getString(R.string.token_okay_key), false) - isOk = isServerUrlOk && isTokenOk - - initialized = true - } - - companion object { - private var holder: APIDataHolder = APIDataHolder() - - fun getInstance(): APIDataHolder { - return holder - } - } -} - -@Serializable -data class AuthDTO( - val id: String, - val name: String, - val createdAt: String -) - -class AuthDataHolder { - var auths: List = listOf() - var authId: String = "" - var authIndex: Int = -1 - - companion object { - private var holder: AuthDataHolder = AuthDataHolder() - - fun getInstance(): AuthDataHolder { - return holder - } - } -} diff --git a/frontend/app/src/main/java/dev/svitan/antifed/AuthActivity.kt b/frontend/app/src/main/java/dev/svitan/antifed/AuthActivity.kt deleted file mode 100644 index 4fec84a..0000000 --- a/frontend/app/src/main/java/dev/svitan/antifed/AuthActivity.kt +++ /dev/null @@ -1,290 +0,0 @@ -package dev.svitan.antifed - -import android.os.Bundle -import android.util.Log -import androidx.activity.ComponentActivity -import androidx.activity.compose.setContent -import androidx.activity.enableEdgeToEdge -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.consumeWindowInsets -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.imePadding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.foundation.verticalScroll -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.automirrored.outlined.ArrowBack -import androidx.compose.material.icons.filled.Visibility -import androidx.compose.material.icons.filled.VisibilityOff -import androidx.compose.material3.CircularProgressIndicator -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Scaffold -import androidx.compose.material3.Text -import androidx.compose.material3.TextField -import androidx.compose.material3.TopAppBar -import androidx.compose.material3.TopAppBarDefaults -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableIntStateOf -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.input.KeyboardType -import androidx.compose.ui.text.input.PasswordVisualTransformation -import androidx.compose.ui.text.input.VisualTransformation -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import androidx.core.content.edit -import dev.svitan.antifed.ui.theme.AntiFedTheme -import kotlinx.coroutines.delay - -@OptIn(ExperimentalMaterial3Api::class) -class AuthActivity : ComponentActivity() { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - enableEdgeToEdge() - - setContent { - AntiFedTheme { - val context = LocalContext.current - val prefs = context.getSharedPreferences( - stringResource(R.string.settings_prefs_key), MODE_PRIVATE - ) - val apiDataHolder = APIDataHolder.getInstance() - apiDataHolder.initializeFromPrefs(context, prefs) - - var serverUrl by remember { mutableStateOf("") } - var token by remember { mutableStateOf("") } - var showToken by remember { mutableStateOf(false) } - - var authId by remember { mutableStateOf("") } - var authIndex by remember { mutableIntStateOf(-1) } - var auths by remember { mutableStateOf>(emptyList()) } - val authDataHolder = AuthDataHolder.getInstance() - - var needToCheckUrl by remember { mutableStateOf(false) } - var checkingUrl by remember { mutableStateOf(false) } - var isServerUrlOk by remember { mutableStateOf(false) } - - var needToCheckToken by remember { mutableStateOf(false) } - var checkingToken by remember { mutableStateOf(false) } - var isTokenOk by remember { mutableStateOf(false) } - - fun serverUrlMatches(): Boolean { - val regex = Regex("""https?://[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}(:\d+)?""") - return regex.matches(serverUrl) - } - - LaunchedEffect(Unit) { - serverUrl = apiDataHolder.serverUrl - token = apiDataHolder.token - isServerUrlOk = prefs.getBoolean(context.getString(R.string.server_url_okay_key), false) - isTokenOk = prefs.getBoolean(context.getString(R.string.token_okay_key), false) - } - - LaunchedEffect(serverUrl) { - apiDataHolder.serverUrl = serverUrl - prefs.edit { - putString( - getString(R.string.server_url_key), serverUrl - ) - } - - if (!needToCheckUrl) return@LaunchedEffect - needToCheckUrl = false - - isServerUrlOk = false - if (serverUrl.isBlank() || !serverUrlMatches()) return@LaunchedEffect - - delay(1000) - checkingUrl = true - - isServerUrlOk = try { - API.doesServerRespond() - } catch (e: Exception) { - Log.e("AuthActivity", "Checking server response failed", e) - false - } - - checkingUrl = false - } - - LaunchedEffect(token) { - apiDataHolder.token = token - prefs.edit { - putString( - getString(R.string.token_key), token - ) - } - - if (!needToCheckToken) return@LaunchedEffect - needToCheckToken = false - isTokenOk = false - - if (!isServerUrlOk || token.isBlank()) return@LaunchedEffect - - delay(1000) - checkingToken = true - - try { - API.loadAuths() - auths = authDataHolder.auths - authIndex = authDataHolder.authIndex - isTokenOk = true - } catch (e: Exception) { - isTokenOk = false - Log.e("AuthActivity", "Error checking token", e) - } - - checkingToken = false - } - - // saves data to prefs and data holders - LaunchedEffect(isServerUrlOk) { - apiDataHolder.isOk = isServerUrlOk && isTokenOk - prefs.edit { - putBoolean( - getString(R.string.server_url_okay_key), - isServerUrlOk - ) - } - } - - LaunchedEffect(isTokenOk) { - apiDataHolder.isOk = isServerUrlOk && isTokenOk - prefs.edit { - putBoolean(getString(R.string.token_okay_key), isTokenOk) - } - } - - LaunchedEffect(authId) { - authDataHolder.authId = authId - prefs.edit { - putString(getString(R.string.auth_id_key), authId) - } - } - - LaunchedEffect(auths, authIndex) { - authDataHolder.auths = auths - authDataHolder.authIndex = authIndex - } - - Scaffold( - topBar = { - TopAppBar( - colors = TopAppBarDefaults.topAppBarColors( - titleContentColor = MaterialTheme.colorScheme.primary - ), - title = { - Text(stringResource(R.string.auth)) - }, - navigationIcon = { - IconButton(onClick = { finish() }) { - Icon( - (Icons.AutoMirrored.Outlined.ArrowBack), - contentDescription = stringResource(R.string.go_back) - ) - } - }) - }) { padding -> - Column( - modifier = Modifier - .fillMaxSize() - .consumeWindowInsets(padding) - .imePadding() - .verticalScroll(rememberScrollState()), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center - ) { - Text(stringResource(R.string.auth), fontSize = 24.sp) - Spacer(modifier = Modifier.height(16.dp)) - - TextField( - value = serverUrl, - onValueChange = { - serverUrl = it - needToCheckUrl = true - }, - label = { Text(stringResource(R.string.server_url)) }, - singleLine = true, - isError = !isServerUrlOk && serverUrl.isNotBlank(), - trailingIcon = { - if (checkingUrl) { - CircularProgressIndicator( - modifier = Modifier.size(24.dp) - ) - } - }) - - Spacer(modifier = Modifier.height(12.dp)) - - TextField( - value = token, - onValueChange = { - token = it - needToCheckToken = true - }, - label = { Text(stringResource(R.string.token)) }, - singleLine = true, - enabled = isServerUrlOk, - isError = !isTokenOk && token.isNotBlank(), - visualTransformation = (if (showToken) { - VisualTransformation.None - } else { - PasswordVisualTransformation() - }), - keyboardOptions = KeyboardOptions( - keyboardType = KeyboardType.Password - ), - trailingIcon = { - if (checkingToken) { - CircularProgressIndicator( - modifier = Modifier.size(36.dp) - ) - } - Icon( - if (showToken) { - Icons.Default.VisibilityOff - } else { - Icons.Default.Visibility - }, - contentDescription = stringResource( - if (showToken) { - R.string.hide_token - } else { - R.string.show_token - } - ), - modifier = Modifier - .align(Alignment.End) - .clickable { showToken = !showToken } - ) - } - ) - } - } - } - } - } - - override fun finish() { - super.finish() - overrideActivityTransition( - OVERRIDE_TRANSITION_CLOSE, - R.anim.slide_in_left, - R.anim.slide_out_right - ) - } -} diff --git a/frontend/app/src/main/java/dev/svitan/antifed/MainActivity.kt b/frontend/app/src/main/java/dev/svitan/antifed/MainActivity.kt deleted file mode 100644 index 619da39..0000000 --- a/frontend/app/src/main/java/dev/svitan/antifed/MainActivity.kt +++ /dev/null @@ -1,183 +0,0 @@ -package dev.svitan.antifed - -import android.content.Intent -import android.os.Bundle -import androidx.activity.ComponentActivity -import androidx.activity.compose.setContent -import androidx.activity.enableEdgeToEdge -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.outlined.AccountCircle -import androidx.compose.material.icons.outlined.Menu -import androidx.compose.material3.DrawerValue -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.HorizontalDivider -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.ModalDrawerSheet -import androidx.compose.material3.ModalNavigationDrawer -import androidx.compose.material3.NavigationDrawerItem -import androidx.compose.material3.Scaffold -import androidx.compose.material3.Text -import androidx.compose.material3.TopAppBar -import androidx.compose.material3.TopAppBarDefaults -import androidx.compose.material3.rememberDrawerState -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp -import dev.svitan.antifed.ui.theme.AntiFedTheme -import kotlinx.coroutines.launch - -@OptIn(ExperimentalMaterial3Api::class) -class MainActivity : ComponentActivity() { - private var reload: (() -> Unit)? = null - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - enableEdgeToEdge() - - setContent { - AntiFedTheme { - val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed) - val scope = rememberCoroutineScope() - - val context = LocalContext.current - val prefs = context.getSharedPreferences( - stringResource(R.string.settings_prefs_key), MODE_PRIVATE - ) - - val apiDataHolder = APIDataHolder.getInstance() - apiDataHolder.initializeFromPrefs(context, prefs) - - // TODO: fix reloading UI - var uiNeedsReload by remember { mutableStateOf(false) } - reload = fun() { uiNeedsReload = true } - - if (!uiNeedsReload) { - ModalNavigationDrawer( - drawerState = drawerState, - drawerContent = { - ModalDrawerSheet { - Column( - modifier = Modifier - .padding(horizontal = 16.dp) - .verticalScroll(rememberScrollState()) - ) { - Spacer(modifier = Modifier.height(24.dp)) - Text( - stringResource(R.string.app_name), - modifier = Modifier.padding(16.dp), - style = MaterialTheme.typography.titleLarge - ) - HorizontalDivider() - - if (apiDataHolder.isOk) { - Text( - stringResource(R.string.actions), - modifier = Modifier.padding(16.dp), - style = MaterialTheme.typography.titleMedium - ) - NavigationDrawerItem( - label = { Text("Item 1") }, - selected = false, - onClick = { /* Handle click */ } - ) - NavigationDrawerItem( - label = { Text("Item 2") }, - selected = false, - onClick = { /* Handle click */ } - ) - } else { - Text( - stringResource(R.string.not_signed_in), - modifier = Modifier.padding(16.dp), - style = MaterialTheme.typography.titleMedium - ) - } - } - } - }, - ) { - Scaffold( - topBar = { - TopAppBar( - colors = TopAppBarDefaults.topAppBarColors( - titleContentColor = MaterialTheme.colorScheme.primary - ), - title = { - Text(stringResource(R.string.app_name)) - }, - navigationIcon = { - IconButton(onClick = { - scope.launch { - if (drawerState.isClosed) { - drawerState.open() - } else { - drawerState.close() - } - } - }) { - Icon( - imageVector = Icons.Outlined.Menu, - contentDescription = stringResource(R.string.show_menu) - ) - } - }, - actions = { - IconButton(onClick = { - val intent = - Intent(this@MainActivity, AuthActivity::class.java) - startActivity(intent) - overridePendingTransition( - R.anim.slide_in_right, - R.anim.slide_out_left - ) - }) { - Icon( - imageVector = Icons.Outlined.AccountCircle, - contentDescription = stringResource(R.string.auth_icon_desc) - ) - } - } - ) - }, - modifier = Modifier.fillMaxSize() - ) { innerPadding -> - Column( - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center, - modifier = Modifier - .padding(innerPadding) - .fillMaxSize() - ) { - Text(stringResource(R.string.app_name)) - } - } - } - } else { - uiNeedsReload = false - } - } - } - } - - override fun onResume() { - super.onResume() - this.reload?.run {} - } -} diff --git a/frontend/app/src/main/java/dev/svitan/antifed/ui/components/DropdownList.kt b/frontend/app/src/main/java/dev/svitan/antifed/ui/components/DropdownList.kt deleted file mode 100644 index c9bb0d1..0000000 --- a/frontend/app/src/main/java/dev/svitan/antifed/ui/components/DropdownList.kt +++ /dev/null @@ -1,92 +0,0 @@ -package dev.svitan.antifed.ui.components - -import androidx.compose.foundation.background -import androidx.compose.foundation.border -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.heightIn -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.Text -import androidx.compose.material3.VerticalDivider -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.unit.dp -import androidx.compose.ui.window.Popup -import androidx.compose.ui.window.PopupProperties - -@Composable -fun DropdownList( - itemList: List, - selectedIndex: Int, - modifier: Modifier = Modifier, - onItemClick: (Int) -> Unit -) { - var showDropdown by rememberSaveable { mutableStateOf(false) } - val scrollState = rememberScrollState() - - Column( - modifier = modifier, - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center - ) { - Box( - modifier = Modifier - .fillMaxWidth() - .background(Color.Red) - .clickable { showDropdown = !showDropdown }, - contentAlignment = Alignment.Center - ) { - Text(text = itemList[selectedIndex], modifier = Modifier.padding(3.dp)) - } - - Box { - if (showDropdown) { - Popup( - alignment = Alignment.TopCenter, - properties = PopupProperties( - excludeFromSystemGesture = true, - ), - onDismissRequest = { showDropdown = false } - ) { - Column( - modifier = modifier - .heightIn(max = 90.dp) - .verticalScroll(state = scrollState) - .border(width = 1.dp, color = Color.Gray), - horizontalAlignment = Alignment.CenterHorizontally, - ) { - itemList.onEachIndexed { index, item -> - if (index != 0) { - VerticalDivider(thickness = 1.dp, color = Color.LightGray) - } - - Box( - modifier = Modifier - .fillMaxWidth() - .background(Color.Green) - .clickable { - onItemClick(index) - showDropdown = !showDropdown - }, - contentAlignment = Alignment.Center - ) { - Text(text = item) - } - } - } - } - } - } - } -} diff --git a/frontend/app/src/main/java/dev/svitan/antifed/ui/theme/Color.kt b/frontend/app/src/main/java/dev/svitan/antifed/ui/theme/Color.kt deleted file mode 100644 index 52c86df..0000000 --- a/frontend/app/src/main/java/dev/svitan/antifed/ui/theme/Color.kt +++ /dev/null @@ -1,75 +0,0 @@ -package dev.svitan.antifed.ui.theme - -import androidx.compose.ui.graphics.Color - -val primaryLight = Color(0xFF5C5891) -val onPrimaryLight = Color(0xFFFFFFFF) -val primaryContainerLight = Color(0xFFE4DFFF) -val onPrimaryContainerLight = Color(0xFF444078) -val secondaryLight = Color(0xFF5E5C71) -val onSecondaryLight = Color(0xFFFFFFFF) -val secondaryContainerLight = Color(0xFFE4DFF9) -val onSecondaryContainerLight = Color(0xFF474559) -val tertiaryLight = Color(0xFF7B5266) -val onTertiaryLight = Color(0xFFFFFFFF) -val tertiaryContainerLight = Color(0xFFFFD8E9) -val onTertiaryContainerLight = Color(0xFF613B4E) -val errorLight = Color(0xFFBA1A1A) -val onErrorLight = Color(0xFFFFFFFF) -val errorContainerLight = Color(0xFFFFDAD6) -val onErrorContainerLight = Color(0xFF93000A) -val backgroundLight = Color(0xFFFCF8FF) -val onBackgroundLight = Color(0xFF1C1B20) -val surfaceLight = Color(0xFFFCF8FF) -val onSurfaceLight = Color(0xFF1C1B20) -val surfaceVariantLight = Color(0xFFE5E1EC) -val onSurfaceVariantLight = Color(0xFF47464F) -val outlineLight = Color(0xFF787680) -val outlineVariantLight = Color(0xFFC8C5D0) -val scrimLight = Color(0xFF000000) -val inverseSurfaceLight = Color(0xFF313036) -val inverseOnSurfaceLight = Color(0xFFF3EFF7) -val inversePrimaryLight = Color(0xFFC5C0FF) -val surfaceDimLight = Color(0xFFDCD8E0) -val surfaceBrightLight = Color(0xFFFCF8FF) -val surfaceContainerLowestLight = Color(0xFFFFFFFF) -val surfaceContainerLowLight = Color(0xFFF6F2FA) -val surfaceContainerLight = Color(0xFFF0ECF4) -val surfaceContainerHighLight = Color(0xFFEBE7EF) -val surfaceContainerHighestLight = Color(0xFFE5E1E9) - -val primaryDark = Color(0xFFC5C0FF) -val onPrimaryDark = Color(0xFF2E2960) -val primaryContainerDark = Color(0xFF444078) -val onPrimaryContainerDark = Color(0xFFE4DFFF) -val secondaryDark = Color(0xFFC8C4DC) -val onSecondaryDark = Color(0xFF302E41) -val secondaryContainerDark = Color(0xFF474559) -val onSecondaryContainerDark = Color(0xFFE4DFF9) -val tertiaryDark = Color(0xFFEBB8D0) -val onTertiaryDark = Color(0xFF472538) -val tertiaryContainerDark = Color(0xFF613B4E) -val onTertiaryContainerDark = Color(0xFFFFD8E9) -val errorDark = Color(0xFFFFB4AB) -val onErrorDark = Color(0xFF690005) -val errorContainerDark = Color(0xFF93000A) -val onErrorContainerDark = Color(0xFFFFDAD6) -val backgroundDark = Color(0xFF131318) -val onBackgroundDark = Color(0xFFE5E1E9) -val surfaceDark = Color(0xFF131318) -val onSurfaceDark = Color(0xFFE5E1E9) -val surfaceVariantDark = Color(0xFF47464F) -val onSurfaceVariantDark = Color(0xFFC8C5D0) -val outlineDark = Color(0xFF928F99) -val outlineVariantDark = Color(0xFF47464F) -val scrimDark = Color(0xFF000000) -val inverseSurfaceDark = Color(0xFFE5E1E9) -val inverseOnSurfaceDark = Color(0xFF313036) -val inversePrimaryDark = Color(0xFF5C5891) -val surfaceDimDark = Color(0xFF131318) -val surfaceBrightDark = Color(0xFF3A383F) -val surfaceContainerLowestDark = Color(0xFF0E0E13) -val surfaceContainerLowDark = Color(0xFF1C1B20) -val surfaceContainerDark = Color(0xFF201F25) -val surfaceContainerHighDark = Color(0xFF2A292F) -val surfaceContainerHighestDark = Color(0xFF35343A) diff --git a/frontend/app/src/main/java/dev/svitan/antifed/ui/theme/Theme.kt b/frontend/app/src/main/java/dev/svitan/antifed/ui/theme/Theme.kt deleted file mode 100644 index 147c2b5..0000000 --- a/frontend/app/src/main/java/dev/svitan/antifed/ui/theme/Theme.kt +++ /dev/null @@ -1,110 +0,0 @@ -package dev.svitan.antifed.ui.theme - -import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.darkColorScheme -import androidx.compose.material3.dynamicDarkColorScheme -import androidx.compose.material3.dynamicLightColorScheme -import androidx.compose.material3.lightColorScheme -import androidx.compose.runtime.Composable -import androidx.compose.ui.platform.LocalContext - -private val lightScheme = lightColorScheme( - primary = primaryLight, - onPrimary = onPrimaryLight, - primaryContainer = primaryContainerLight, - onPrimaryContainer = onPrimaryContainerLight, - secondary = secondaryLight, - onSecondary = onSecondaryLight, - secondaryContainer = secondaryContainerLight, - onSecondaryContainer = onSecondaryContainerLight, - tertiary = tertiaryLight, - onTertiary = onTertiaryLight, - tertiaryContainer = tertiaryContainerLight, - onTertiaryContainer = onTertiaryContainerLight, - error = errorLight, - onError = onErrorLight, - errorContainer = errorContainerLight, - onErrorContainer = onErrorContainerLight, - background = backgroundLight, - onBackground = onBackgroundLight, - surface = surfaceLight, - onSurface = onSurfaceLight, - surfaceVariant = surfaceVariantLight, - onSurfaceVariant = onSurfaceVariantLight, - outline = outlineLight, - outlineVariant = outlineVariantLight, - scrim = scrimLight, - inverseSurface = inverseSurfaceLight, - inverseOnSurface = inverseOnSurfaceLight, - inversePrimary = inversePrimaryLight, - surfaceDim = surfaceDimLight, - surfaceBright = surfaceBrightLight, - surfaceContainerLowest = surfaceContainerLowestLight, - surfaceContainerLow = surfaceContainerLowLight, - surfaceContainer = surfaceContainerLight, - surfaceContainerHigh = surfaceContainerHighLight, - surfaceContainerHighest = surfaceContainerHighestLight, -) - -private val darkScheme = darkColorScheme( - primary = primaryDark, - onPrimary = onPrimaryDark, - primaryContainer = primaryContainerDark, - onPrimaryContainer = onPrimaryContainerDark, - secondary = secondaryDark, - onSecondary = onSecondaryDark, - secondaryContainer = secondaryContainerDark, - onSecondaryContainer = onSecondaryContainerDark, - tertiary = tertiaryDark, - onTertiary = onTertiaryDark, - tertiaryContainer = tertiaryContainerDark, - onTertiaryContainer = onTertiaryContainerDark, - error = errorDark, - onError = onErrorDark, - errorContainer = errorContainerDark, - onErrorContainer = onErrorContainerDark, - background = backgroundDark, - onBackground = onBackgroundDark, - surface = surfaceDark, - onSurface = onSurfaceDark, - surfaceVariant = surfaceVariantDark, - onSurfaceVariant = onSurfaceVariantDark, - outline = outlineDark, - outlineVariant = outlineVariantDark, - scrim = scrimDark, - inverseSurface = inverseSurfaceDark, - inverseOnSurface = inverseOnSurfaceDark, - inversePrimary = inversePrimaryDark, - surfaceDim = surfaceDimDark, - surfaceBright = surfaceBrightDark, - surfaceContainerLowest = surfaceContainerLowestDark, - surfaceContainerLow = surfaceContainerLowDark, - surfaceContainer = surfaceContainerDark, - surfaceContainerHigh = surfaceContainerHighDark, - surfaceContainerHighest = surfaceContainerHighestDark, -) - -@Composable -fun AntiFedTheme( - darkTheme: Boolean = isSystemInDarkTheme(), - // Dynamic color is available on Android 12+ - dynamicColor: Boolean = true, - content: @Composable () -> Unit -) { - val colorScheme = when { - dynamicColor -> { - val context = LocalContext.current - if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) - } - - darkTheme -> darkScheme - else -> lightScheme - } - - MaterialTheme( - colorScheme = colorScheme, - typography = Typography, - content = content - ) -} diff --git a/frontend/app/src/main/java/dev/svitan/antifed/ui/theme/Type.kt b/frontend/app/src/main/java/dev/svitan/antifed/ui/theme/Type.kt deleted file mode 100644 index 036d1d7..0000000 --- a/frontend/app/src/main/java/dev/svitan/antifed/ui/theme/Type.kt +++ /dev/null @@ -1,34 +0,0 @@ -package dev.svitan.antifed.ui.theme - -import androidx.compose.material3.Typography -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.font.FontFamily -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.sp - -// Set of Material typography styles to start with -val Typography = Typography( - bodyLarge = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.Normal, - fontSize = 16.sp, - lineHeight = 24.sp, - letterSpacing = 0.5.sp - ) - /* Other default text styles to override - titleLarge = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.Normal, - fontSize = 22.sp, - lineHeight = 28.sp, - letterSpacing = 0.sp - ), - labelSmall = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.Medium, - fontSize = 11.sp, - lineHeight = 16.sp, - letterSpacing = 0.5.sp - ) - */ -) \ No newline at end of file diff --git a/frontend/app/src/main/res/anim/slide_in_left.xml b/frontend/app/src/main/res/anim/slide_in_left.xml deleted file mode 100644 index dcc2795..0000000 --- a/frontend/app/src/main/res/anim/slide_in_left.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - \ No newline at end of file diff --git a/frontend/app/src/main/res/anim/slide_in_right.xml b/frontend/app/src/main/res/anim/slide_in_right.xml deleted file mode 100644 index 03f9164..0000000 --- a/frontend/app/src/main/res/anim/slide_in_right.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - \ No newline at end of file diff --git a/frontend/app/src/main/res/anim/slide_out_left.xml b/frontend/app/src/main/res/anim/slide_out_left.xml deleted file mode 100644 index 7e11c3f..0000000 --- a/frontend/app/src/main/res/anim/slide_out_left.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - \ No newline at end of file diff --git a/frontend/app/src/main/res/anim/slide_out_right.xml b/frontend/app/src/main/res/anim/slide_out_right.xml deleted file mode 100644 index cc54fd8..0000000 --- a/frontend/app/src/main/res/anim/slide_out_right.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - \ No newline at end of file diff --git a/frontend/app/src/main/res/drawable/background.png b/frontend/app/src/main/res/drawable/background.png deleted file mode 100644 index b20f8a9..0000000 Binary files a/frontend/app/src/main/res/drawable/background.png and /dev/null differ diff --git a/frontend/app/src/main/res/drawable/foreground.png b/frontend/app/src/main/res/drawable/foreground.png deleted file mode 100644 index 6fd0808..0000000 Binary files a/frontend/app/src/main/res/drawable/foreground.png and /dev/null differ diff --git a/frontend/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/frontend/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml deleted file mode 100644 index 4ae7d12..0000000 --- a/frontend/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/frontend/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/frontend/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml deleted file mode 100644 index 4ae7d12..0000000 --- a/frontend/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/frontend/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/frontend/app/src/main/res/mipmap-hdpi/ic_launcher.webp deleted file mode 100644 index 9fc6a14..0000000 Binary files a/frontend/app/src/main/res/mipmap-hdpi/ic_launcher.webp and /dev/null differ diff --git a/frontend/app/src/main/res/mipmap-hdpi/ic_launcher_background.webp b/frontend/app/src/main/res/mipmap-hdpi/ic_launcher_background.webp deleted file mode 100644 index 38eea84..0000000 Binary files a/frontend/app/src/main/res/mipmap-hdpi/ic_launcher_background.webp and /dev/null differ diff --git a/frontend/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp b/frontend/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp deleted file mode 100644 index 58b0fb1..0000000 Binary files a/frontend/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp and /dev/null differ diff --git a/frontend/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/frontend/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp deleted file mode 100644 index f31a1dc..0000000 Binary files a/frontend/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp and /dev/null differ diff --git a/frontend/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/frontend/app/src/main/res/mipmap-mdpi/ic_launcher.webp deleted file mode 100644 index 1069c1d..0000000 Binary files a/frontend/app/src/main/res/mipmap-mdpi/ic_launcher.webp and /dev/null differ diff --git a/frontend/app/src/main/res/mipmap-mdpi/ic_launcher_background.webp b/frontend/app/src/main/res/mipmap-mdpi/ic_launcher_background.webp deleted file mode 100644 index e7026ff..0000000 Binary files a/frontend/app/src/main/res/mipmap-mdpi/ic_launcher_background.webp and /dev/null differ diff --git a/frontend/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp b/frontend/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp deleted file mode 100644 index c0b326d..0000000 Binary files a/frontend/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp and /dev/null differ diff --git a/frontend/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/frontend/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp deleted file mode 100644 index 6095824..0000000 Binary files a/frontend/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp and /dev/null differ diff --git a/frontend/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/frontend/app/src/main/res/mipmap-xhdpi/ic_launcher.webp deleted file mode 100644 index 3c85d9a..0000000 Binary files a/frontend/app/src/main/res/mipmap-xhdpi/ic_launcher.webp and /dev/null differ diff --git a/frontend/app/src/main/res/mipmap-xhdpi/ic_launcher_background.webp b/frontend/app/src/main/res/mipmap-xhdpi/ic_launcher_background.webp deleted file mode 100644 index 3ba2f4c..0000000 Binary files a/frontend/app/src/main/res/mipmap-xhdpi/ic_launcher_background.webp and /dev/null differ diff --git a/frontend/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp b/frontend/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp deleted file mode 100644 index c5a02f0..0000000 Binary files a/frontend/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp and /dev/null differ diff --git a/frontend/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/frontend/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp deleted file mode 100644 index a52a43d..0000000 Binary files a/frontend/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp and /dev/null differ diff --git a/frontend/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/frontend/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp deleted file mode 100644 index 10f1a74..0000000 Binary files a/frontend/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp and /dev/null differ diff --git a/frontend/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.webp b/frontend/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.webp deleted file mode 100644 index d09811a..0000000 Binary files a/frontend/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.webp and /dev/null differ diff --git a/frontend/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp b/frontend/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp deleted file mode 100644 index 85ae584..0000000 Binary files a/frontend/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp and /dev/null differ diff --git a/frontend/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/frontend/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp deleted file mode 100644 index 7a9807e..0000000 Binary files a/frontend/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp and /dev/null differ diff --git a/frontend/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/frontend/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp deleted file mode 100644 index 42a10ea..0000000 Binary files a/frontend/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp and /dev/null differ diff --git a/frontend/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.webp b/frontend/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.webp deleted file mode 100644 index afaaa72..0000000 Binary files a/frontend/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.webp and /dev/null differ diff --git a/frontend/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp b/frontend/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp deleted file mode 100644 index 46a3bf9..0000000 Binary files a/frontend/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp and /dev/null differ diff --git a/frontend/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/frontend/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp deleted file mode 100644 index d2adb96..0000000 Binary files a/frontend/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp and /dev/null differ diff --git a/frontend/app/src/main/res/values-sk/strings.xml b/frontend/app/src/main/res/values-sk/strings.xml deleted file mode 100644 index 31bc585..0000000 --- a/frontend/app/src/main/res/values-sk/strings.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - URL serveru - AntiFed autentikácia - Ukázať menu - Ísť do nastavení autentikácie - Ísť späť - Token - Vytvoriť novú auth - Biometrika - Ukázať token - Skryť token - Akcie - Nie ste prihlásený*á - \ No newline at end of file diff --git a/frontend/app/src/main/res/values/colors.xml b/frontend/app/src/main/res/values/colors.xml deleted file mode 100644 index f8c6127..0000000 --- a/frontend/app/src/main/res/values/colors.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - #FFBB86FC - #FF6200EE - #FF3700B3 - #FF03DAC5 - #FF018786 - #FF000000 - #FFFFFFFF - \ No newline at end of file diff --git a/frontend/app/src/main/res/values/strings.xml b/frontend/app/src/main/res/values/strings.xml deleted file mode 100644 index d80f556..0000000 --- a/frontend/app/src/main/res/values/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ - - AntiFed - Server URL - AntiFed auth - Show menu - Go to auth settings - Go back - Token - dev.svitan.antifed.settings - server_url - server_url_okay - token - auth_id - Create new auth - Biometrics - Show token - Hide token - token_okay - Actions - Not signed in - - - \ No newline at end of file diff --git a/frontend/app/src/main/res/values/themes.xml b/frontend/app/src/main/res/values/themes.xml deleted file mode 100644 index e1f6e3a..0000000 --- a/frontend/app/src/main/res/values/themes.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - -