From 948109cd9629af140a3f5144a8c4d799b3d363a2 Mon Sep 17 00:00:00 2001 From: Arindy Date: Tue, 22 Oct 2024 20:53:00 +0200 Subject: [PATCH] Initial Commit --- .github/workflows/ci.yaml | 26 ++ .gitignore | 9 + .mvn/wrapper/.gitignore | 1 + .mvn/wrapper/MavenWrapperDownloader.java | 93 +++++ .mvn/wrapper/maven-wrapper.properties | 20 ++ compose.yml | 11 + mvnw | 332 ++++++++++++++++++ pom.xml | 206 +++++++++++ src/main/docker/Dockerfile.native | 11 + src/main/docker/Dockerfile.native-micro | 11 + .../mythodea/api/MitrasperanCalendar.kt | 248 +++++++++++++ .../de/arindy/mythodea/api/MitrasperanDate.kt | 78 ++++ .../mythodea/api/MythodeaDateResource.kt | 19 + src/main/resources/application.properties | 1 + 14 files changed, 1066 insertions(+) create mode 100644 .github/workflows/ci.yaml create mode 100644 .gitignore create mode 100644 .mvn/wrapper/.gitignore create mode 100644 .mvn/wrapper/MavenWrapperDownloader.java create mode 100644 .mvn/wrapper/maven-wrapper.properties create mode 100644 compose.yml create mode 100755 mvnw create mode 100644 pom.xml create mode 100644 src/main/docker/Dockerfile.native create mode 100644 src/main/docker/Dockerfile.native-micro create mode 100644 src/main/kotlin/de/arindy/mythodea/api/MitrasperanCalendar.kt create mode 100644 src/main/kotlin/de/arindy/mythodea/api/MitrasperanDate.kt create mode 100644 src/main/kotlin/de/arindy/mythodea/api/MythodeaDateResource.kt create mode 100644 src/main/resources/application.properties diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..f3aee33 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,26 @@ +name: CI +on: [ push ] + +jobs: + deploy: + env: + REPO: ${{ github.event.repository.name }} + runs-on: ubuntu-latest + steps: + - name: Check out repository code + uses: actions/checkout@v4 + + - name: Setup JDK + uses: actions/setup-java@v4 + with: + distribution: 'zulu' + java-version: '21' + + - name: Build Runner + run: ./mvnw --no-transfer-progress clean verify -Pnative -Dquarkus.native.remote-container-build=true + + - name: Build Container + run: docker build -f src/main/docker/Dockerfile.native-micro -t ${{ env.REPO }} . + + - name: Deploy + run: "docker compose up -d" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ad5e79f --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +*~ + +searxng-docker.service +caddy +srv +searxng/uwsgi.ini + +.idea +target diff --git a/.mvn/wrapper/.gitignore b/.mvn/wrapper/.gitignore new file mode 100644 index 0000000..e72f5e8 --- /dev/null +++ b/.mvn/wrapper/.gitignore @@ -0,0 +1 @@ +maven-wrapper.jar diff --git a/.mvn/wrapper/MavenWrapperDownloader.java b/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 0000000..fe7d037 --- /dev/null +++ b/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,93 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ + +import java.io.IOException; +import java.io.InputStream; +import java.net.Authenticator; +import java.net.PasswordAuthentication; +import java.net.URI; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.concurrent.ThreadLocalRandom; + +public final class MavenWrapperDownloader { + private static final String WRAPPER_VERSION = "3.3.2"; + + private static final boolean VERBOSE = Boolean.parseBoolean(System.getenv("MVNW_VERBOSE")); + + public static void main(String[] args) { + log("Apache Maven Wrapper Downloader " + WRAPPER_VERSION); + + if (args.length != 2) { + System.err.println(" - ERROR wrapperUrl or wrapperJarPath parameter missing"); + System.exit(1); + } + + try { + log(" - Downloader started"); + final URL wrapperUrl = URI.create(args[0]).toURL(); + final String jarPath = args[1].replace("..", ""); // Sanitize path + final Path wrapperJarPath = Paths.get(jarPath).toAbsolutePath().normalize(); + downloadFileFromURL(wrapperUrl, wrapperJarPath); + log("Done"); + } catch (IOException e) { + System.err.println("- Error downloading: " + e.getMessage()); + if (VERBOSE) { + e.printStackTrace(); + } + System.exit(1); + } + } + + private static void downloadFileFromURL(URL wrapperUrl, Path wrapperJarPath) + throws IOException { + log(" - Downloading to: " + wrapperJarPath); + if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { + final String username = System.getenv("MVNW_USERNAME"); + final char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); + Authenticator.setDefault(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, password); + } + }); + } + Path temp = wrapperJarPath + .getParent() + .resolve(wrapperJarPath.getFileName() + "." + + Long.toUnsignedString(ThreadLocalRandom.current().nextLong()) + ".tmp"); + try (InputStream inStream = wrapperUrl.openStream()) { + Files.copy(inStream, temp, StandardCopyOption.REPLACE_EXISTING); + Files.move(temp, wrapperJarPath, StandardCopyOption.REPLACE_EXISTING); + } finally { + Files.deleteIfExists(temp); + } + log(" - Downloader complete"); + } + + private static void log(String msg) { + if (VERBOSE) { + System.out.println(msg); + } + } + +} diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..72df6c6 --- /dev/null +++ b/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,20 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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 +# +# http://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. +wrapperVersion=3.3.2 +distributionType=source +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.8/apache-maven-3.9.8-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar \ No newline at end of file diff --git a/compose.yml b/compose.yml new file mode 100644 index 0000000..1946e52 --- /dev/null +++ b/compose.yml @@ -0,0 +1,11 @@ +services: + api: + container_name: ${REPO} + image: ${REPO} + restart: always + networks: + - reverse_proxy + +networks: + reverse_proxy: + external: true diff --git a/mvnw b/mvnw new file mode 100755 index 0000000..5e9618c --- /dev/null +++ b/mvnw @@ -0,0 +1,332 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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 +# +# http://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. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, version 3.3.2 +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ]; then + + if [ -f /usr/local/etc/mavenrc ]; then + . /usr/local/etc/mavenrc + fi + + if [ -f /etc/mavenrc ]; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ]; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false +darwin=false +mingw=false +case "$(uname)" in +CYGWIN*) cygwin=true ;; +MINGW*) mingw=true ;; +Darwin*) + darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + JAVA_HOME="$(/usr/libexec/java_home)" + export JAVA_HOME + else + JAVA_HOME="/Library/Java/Home" + export JAVA_HOME + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ]; then + if [ -r /etc/gentoo-release ]; then + JAVA_HOME=$(java-config --jre-home) + fi +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin; then + [ -n "$JAVA_HOME" ] \ + && JAVA_HOME=$(cygpath --unix "$JAVA_HOME") + [ -n "$CLASSPATH" ] \ + && CLASSPATH=$(cygpath --path --unix "$CLASSPATH") +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw; then + [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] \ + && JAVA_HOME="$( + cd "$JAVA_HOME" || ( + echo "cannot cd into $JAVA_HOME." >&2 + exit 1 + ) + pwd + )" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="$(which javac)" + if [ -n "$javaExecutable" ] && ! [ "$(expr "$javaExecutable" : '\([^ ]*\)')" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=$(which readlink) + if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then + if $darwin; then + javaHome="$(dirname "$javaExecutable")" + javaExecutable="$(cd "$javaHome" && pwd -P)/javac" + else + javaExecutable="$(readlink -f "$javaExecutable")" + fi + javaHome="$(dirname "$javaExecutable")" + javaHome=$(expr "$javaHome" : '\(.*\)/bin') + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ]; then + 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 + else + JAVACMD="$( + \unset -f command 2>/dev/null + \command -v java + )" + fi +fi + +if [ ! -x "$JAVACMD" ]; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ]; then + echo "Warning: JAVA_HOME environment variable is not set." >&2 +fi + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + if [ -z "$1" ]; then + echo "Path not specified to find_maven_basedir" >&2 + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ]; do + if [ -d "$wdir"/.mvn ]; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=$( + cd "$wdir/.." || exit 1 + pwd + ) + fi + # end of workaround + done + printf '%s' "$( + cd "$basedir" || exit 1 + pwd + )" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + # Remove \r in case we run on Windows within Git Bash + # and check out the repository with auto CRLF management + # enabled. Otherwise, we may read lines that are delimited with + # \r\n and produce $'-Xarg\r' rather than -Xarg due to word + # splitting rules. + tr -s '\r\n' ' ' <"$1" + fi +} + +log() { + if [ "$MVNW_VERBOSE" = true ]; then + printf '%s\n' "$1" + fi +} + +BASE_DIR=$(find_maven_basedir "$(dirname "$0")") +if [ -z "$BASE_DIR" ]; then + exit 1 +fi + +MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +export MAVEN_PROJECTBASEDIR +log "$MAVEN_PROJECTBASEDIR" + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" +if [ -r "$wrapperJarPath" ]; then + log "Found $wrapperJarPath" +else + log "Couldn't find $wrapperJarPath, downloading it ..." + + if [ -n "$MVNW_REPOURL" ]; then + wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar" + else + wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar" + fi + while IFS="=" read -r key value; do + # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' ) + safeValue=$(echo "$value" | tr -d '\r') + case "$key" in wrapperUrl) + wrapperUrl="$safeValue" + break + ;; + esac + done <"$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" + log "Downloading from: $wrapperUrl" + + if $cygwin; then + wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath") + fi + + if command -v wget >/dev/null; then + log "Found wget ... using wget" + [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + else + wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + fi + elif command -v curl >/dev/null; then + log "Found curl ... using curl" + [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" + else + curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" + fi + else + log "Falling back to using Java to download" + javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java" + javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaSource=$(cygpath --path --windows "$javaSource") + javaClass=$(cygpath --path --windows "$javaClass") + fi + if [ -e "$javaSource" ]; then + if [ ! -e "$javaClass" ]; then + log " - Compiling MavenWrapperDownloader.java ..." + ("$JAVA_HOME/bin/javac" "$javaSource") + fi + if [ -e "$javaClass" ]; then + log " - Running MavenWrapperDownloader.java ..." + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath" + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +# If specified, validate the SHA-256 sum of the Maven wrapper jar file +wrapperSha256Sum="" +while IFS="=" read -r key value; do + case "$key" in wrapperSha256Sum) + wrapperSha256Sum=$value + break + ;; + esac +done <"$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" +if [ -n "$wrapperSha256Sum" ]; then + wrapperSha256Result=false + if command -v sha256sum >/dev/null; then + if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c >/dev/null 2>&1; then + wrapperSha256Result=true + fi + elif command -v shasum >/dev/null; then + if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c >/dev/null 2>&1; then + wrapperSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 + echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + fi + if [ $wrapperSha256Result = false ]; then + echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2 + echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2 + echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2 + exit 1 + fi +fi + +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$JAVA_HOME" ] \ + && JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME") + [ -n "$CLASSPATH" ] \ + && CLASSPATH=$(cygpath --path --windows "$CLASSPATH") + [ -n "$MAVEN_PROJECTBASEDIR" ] \ + && MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR") +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +# shellcheck disable=SC2086 # safe args +exec "$JAVACMD" \ + $MAVEN_OPTS \ + $MAVEN_DEBUG_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..5d2979b --- /dev/null +++ b/pom.xml @@ -0,0 +1,206 @@ + + + 4.0.0 + de.arindy + mythodea-api + 1.0.0-SNAPSHOT + + + 3.13.0 + 2.0.21 + 21 + UTF-8 + UTF-8 + quarkus-bom + io.quarkus.platform + 3.15.1 + true + 3.3.1 + + + + + + ${quarkus.platform.group-id} + ${quarkus.platform.artifact-id} + ${quarkus.platform.version} + pom + import + + + + + + + io.quarkus + quarkus-rest-jackson + + + io.quarkus + quarkus-kotlin + + + io.quarkus + quarkus-arc + + + io.quarkus + quarkus-rest + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + io.rest-assured + kotlin-extensions + test + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + ${kotlin.version} + + + org.jetbrains.kotlin + kotlin-test + ${kotlin.version} + test + + + + + src/main/kotlin + src/test/kotlin + + + ${quarkus.platform.group-id} + quarkus-maven-plugin + ${quarkus.platform.version} + true + + + + build + generate-code + generate-code-tests + native-image-agent + + + + + + maven-compiler-plugin + ${compiler-plugin.version} + + true + + + + maven-surefire-plugin + ${surefire-plugin.version} + + + org.jboss.logmanager.LogManager + ${maven.home} + + + + + maven-failsafe-plugin + ${surefire-plugin.version} + + + + integration-test + verify + + + + + + ${project.build.directory}/${project.build.finalName}-runner + + org.jboss.logmanager.LogManager + ${maven.home} + + + + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + + compile + compile + + compile + + + + src/main/kotlin + target/generated-sources/annotations + + + + + test-compile + test-compile + + test-compile + + + + src/test/kotlin + target/generated-test-sources/test-annotations + + + + + + + org.jetbrains.kotlin + kotlin-maven-allopen + ${kotlin.version} + + + + true + 1.8 + + all-open + + + + + + + + + + + + + + + native + + + native + + + + false + true + + + + diff --git a/src/main/docker/Dockerfile.native b/src/main/docker/Dockerfile.native new file mode 100644 index 0000000..00294c2 --- /dev/null +++ b/src/main/docker/Dockerfile.native @@ -0,0 +1,11 @@ +FROM registry.access.redhat.com/ubi8/ubi-minimal:8.10 +WORKDIR /work/ +RUN chown 1001 /work \ + && chmod "g+rwX" /work \ + && chown 1001:root /work +COPY --chown=1001:root target/*-runner /work/application + +EXPOSE 8080 +USER 1001 + +ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"] diff --git a/src/main/docker/Dockerfile.native-micro b/src/main/docker/Dockerfile.native-micro new file mode 100644 index 0000000..fb244ab --- /dev/null +++ b/src/main/docker/Dockerfile.native-micro @@ -0,0 +1,11 @@ +FROM quay.io/quarkus/quarkus-micro-image:2.0 +WORKDIR /work/ +RUN chown 1001 /work \ + && chmod "g+rwX" /work \ + && chown 1001:root /work +COPY --chown=1001:root target/*-runner /work/application + +EXPOSE 8080 +USER 1001 + +ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"] diff --git a/src/main/kotlin/de/arindy/mythodea/api/MitrasperanCalendar.kt b/src/main/kotlin/de/arindy/mythodea/api/MitrasperanCalendar.kt new file mode 100644 index 0000000..e22d928 --- /dev/null +++ b/src/main/kotlin/de/arindy/mythodea/api/MitrasperanCalendar.kt @@ -0,0 +1,248 @@ +package de.arindy.mythodea.api + +import java.util.Calendar +import java.util.Calendar.DAY_OF_MONTH +import java.util.Calendar.HOUR_OF_DAY +import java.util.Calendar.MINUTE +import java.util.Calendar.MONTH +import java.util.Calendar.SECOND +import java.util.Calendar.YEAR +import java.util.TimeZone + +class MitrasperanCalendar { + + fun get(calendar: Calendar): MitrasperanDate { + calendar.set(HOUR_OF_DAY, 12) + calendar.set(MINUTE, 0) + calendar.set(SECOND, 0) + val moladStart = Calendar.getInstance(TimeZone.getTimeZone("Europe/Berlin")) + moladStart.set(2045, 10, 10, 0, 0, 0) + val moladEnd = Calendar.getInstance(TimeZone.getTimeZone("Europe/Berlin")) + moladEnd.set(2046, 10, 30, 0, 0, 0) + var startOfMolad = false + if (moladStart.get(YEAR) == calendar.get(YEAR) && moladStart.get(MONTH) == calendar.get(MONTH) && moladStart.get( + DAY_OF_MONTH + ) == calendar.get(DAY_OF_MONTH) + ) { + startOfMolad = true + } + if (calendar.after(moladStart) && calendar.before(moladEnd)) { + calendar.add(Calendar.DATE, -1) + } + val absDate = absoluteFromGregorianDate(calendar) + var y: Int + var m: Int + var day: Int + var temp: Int + + /* Approximation */ + val approx = (absDate + 1373429) / 366 + + /* Search forward from the approximation */ + y = approx + while (true) { + temp = absoluteFromMitrasperanDate(MitrasperanDate(1, 7, y + 1)) + if (absDate < temp) break + y++ + } + val year = y + + /* Starting month for search for month */ + temp = absoluteFromMitrasperanDate(MitrasperanDate(1, 1, year)) + val start = if (absDate < temp) 7 + else 1 + + /* Search forward from either Tishri or Nisan */ + m = start + while (true) { + temp = absoluteFromMitrasperanDate(MitrasperanDate(getLastDayOfMitrasperanMonth(m, year), m, year)) + if (absDate <= temp) break + m++ + } + val month = m + + /* Calculate the day by subtraction */ + temp = absoluteFromMitrasperanDate(MitrasperanDate(1, month, year)) + day = absDate - temp + 1 + + if (startOfMolad) { + day++ + } + + return MitrasperanDate(day, if (month == 12 && !leapYear(year)) 13 else month, year(year), calendar.get(MONTH)) + } + + private fun leapYear(year: Int): Boolean { + return ((7 * year) + 1) % 19 < 7 + } + + private fun getLastDayOfGregorianMonth(month: Int, year: Int): Int { + return if ((month == 2) && + ((year % 4) == 0) && + ((year % 400) != 100) && + ((year % 400) != 200) && + ((year % 400) != 300) + ) 29 + else intArrayOf(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)[month - 1] + } + + private fun year(year: Int): Int { + return year - 5763 + } + + private fun absoluteFromGregorianDate(date: Calendar): Int { + /* Days so far this month */ + var value = date.get(DAY_OF_MONTH) + + /* Days in prior months this year */ + var m = 1 + while (m < date.get(MONTH) + 1) { + value += getLastDayOfGregorianMonth(m, date.get(YEAR)) + m++ + } + + /* Days in prior years */ + value += (365 * (date.get(YEAR) - 1)) + + /* Julian leap days in prior years ... */ + value += ((date.get(YEAR) - 1) / 4) + + /* ... minus prior century years ... */ + value -= ((date.get(YEAR) - 1) / 100) + + /* ... plus prior years divisible by 400 */ + value += ((date.get(YEAR) - 1) / 400) + + return (value) + } + + private fun absoluteFromMitrasperanDate(date: MitrasperanDate): Int { + var value: Int + var m: Int + + /* Days so far this month */ + value = date.day + var returnValue = value + + /* If before Tishri */ + if (date.month < 7) { + /* Then add days in prior months this year before and */ + /* after Nisan. */ + m = 7 + while (m <= getLastMonthOfMitrasperanYear(date.year)) { + value = getLastDayOfMitrasperanMonth(m, date.year) + returnValue += value + m++ + } + m = 1 + while (m < date.month) { + value = getLastDayOfMitrasperanMonth(m, date.year) + returnValue += value + m++ + } + } else { + m = 7 + while (m < date.month) { + value = getLastDayOfMitrasperanMonth(m, date.year) + returnValue += value + m++ + } + } + + /* Days in prior years */ + value = mitrasperanCalendarElapsedDays(date.year) + returnValue += value + + /* Days elapsed before absolute date 1 */ + value = 1373429 + returnValue -= value + + return (returnValue) + } + + private fun getLastMonthOfMitrasperanYear(year: Int): Int { + return if (leapYear(year)) 13 + else 12 + } + + private fun getLastDayOfMitrasperanMonth(month: Int, year: Int): Int { + if ((month == 2) || + (month == 4) || + (month == 6) || + (month == 10) || + (month == 13) + ) return 29 + if ((month == 12) && (!leapYear(year))) return 29 + if ((month == 8) && (!longYear(year))) return 29 + if ((month == 9) && (shortYear(year))) return 29 + return 30 + } + + private fun daysInMitrasperanYear(year: Int): Int { + return (mitrasperanCalendarElapsedDays(year + 1) - + mitrasperanCalendarElapsedDays(year)) + } + + private fun longYear(year: Int): Boolean { + return (daysInMitrasperanYear(year) % 10) == 5 + } + + private fun shortYear(year: Int): Boolean { + return (daysInMitrasperanYear(year) % 10) == 3 + } + + private fun mitrasperanCalendarElapsedDays(year: Int): Int { + var monthsElapsed: Int + val hoursElapsed: Int + val parts: Int + var alternativeDay: Int + + /* Months in complete cycles so far */ + var value = 235 * ((year - 1) / 19) + monthsElapsed = value + + /* Regular months in this cycle */ + value = 12 * ((year - 1) % 19) + monthsElapsed += value + + /* Leap months this cycle */ + value = ((((year - 1) % 19) * 7) + 1) / 19 + monthsElapsed += value + + val partsElapsed = (((monthsElapsed % 1080) * 793) + 204) + hoursElapsed = (5 + + (monthsElapsed * 12) + + ((monthsElapsed / 1080) * 793) + + (partsElapsed / 1080)) + + /* Conjunction day */ + val day = 1 + (29 * monthsElapsed) + (hoursElapsed / 24) + + /* Conjunction parts */ + parts = ((hoursElapsed % 24) * 1080) + + (partsElapsed % 1080) + + /* If new moon is at or after midday, */ + alternativeDay = if ((parts >= 19440) || /* ...or is on a Tuesday... */ + (((day % 7) == 2) && /* at 9 hours, 204 parts or later */ + (parts >= 9924) && /* of a common year */ + (!leapYear(year))) || /* ...or is on a Monday at... */ + (((day % 7) == 1) && /* 15 hours, 589 parts or later... */ + (parts >= 16789) && /* at the end of a leap year */ + (leapYear(year - 1))) + ) /* Then postpone Rosh HaShanah one day */ + day + 1 + else day + + /* If Rosh HaShanah would occur on Sunday, Wednesday, */ + /* or Friday */ + if (((alternativeDay % 7) == 0) || + ((alternativeDay % 7) == 3) || + ((alternativeDay % 7) == 5) + ) /* Then postpone it one (more) day and return */ + alternativeDay++ + + return (alternativeDay) + } + +} diff --git a/src/main/kotlin/de/arindy/mythodea/api/MitrasperanDate.kt b/src/main/kotlin/de/arindy/mythodea/api/MitrasperanDate.kt new file mode 100644 index 0000000..bf73f7a --- /dev/null +++ b/src/main/kotlin/de/arindy/mythodea/api/MitrasperanDate.kt @@ -0,0 +1,78 @@ +package de.arindy.mythodea.api + +import io.quarkus.logging.Log +import java.time.Instant +import java.util.Calendar +import java.util.Date +import java.util.TimeZone + +data class MitrasperanDate internal constructor( + val day: Int, + val month: Int, + val year: Int, + private val gregorianMonth: Int, + val yearNDE: Int = if (month <= 6 || gregorianMonth >= 7) year + 1 else year, + val yearNDK: Int = year - 19, + val dayString: String = DAYS[day - 1], + val monthString: String = MONTHS[month - 1] +) { + + constructor(day: Int, month: Int, year: Int) : this(day, month, year, -1) + + companion object { + private val MONTHS = arrayOf( + "Wandelmond", + "Launing", + "Blütenmond", + "Brachmond", + "Weidmond", + "Naiba", + "Holzmond", + "Goldmond", + "Scheiding", + "Blaumond", + "Gilbhart", + "Eismond", + "Fralt", + ) + private val DAYS = arrayOf( + "1. Erztag", + "1. Fyrstag", + "1. Bindetag", + "1. Meerstag", + "1. Wintstag", + "1. Mahntag", + "2. Erztag", + "2. Fyrstag", + "2. Bindetag", + "2. Meerstag", + "2. Wintstag", + "2. Mahntag", + "Weiße Vornacht", + "Weiße Nacht", + "Weiße Nacht", + "3. Erztag", + "3. Fyrstag", + "3. Bindetag", + "3. Meerstag", + "3. Wintstag", + "3. Mahntag", + "4. Erztag", + "4. Fyrstag", + "4. Bindetag", + "4. Meerstag", + "4. Wintstag", + "4. Mahntag", + "Schwarze Nacht", + "Schwarze Nacht", + "Pechnacht", + ) + } +} + +fun mitrasperan(date: Instant): MitrasperanDate { + val calendar = Calendar.getInstance(TimeZone.getTimeZone("Europe/Berlin")) + calendar.time = Date.from(date) + Log.info(calendar.time.toString()) + return MitrasperanCalendar().get(calendar) +} diff --git a/src/main/kotlin/de/arindy/mythodea/api/MythodeaDateResource.kt b/src/main/kotlin/de/arindy/mythodea/api/MythodeaDateResource.kt new file mode 100644 index 0000000..52d8eaa --- /dev/null +++ b/src/main/kotlin/de/arindy/mythodea/api/MythodeaDateResource.kt @@ -0,0 +1,19 @@ +package de.arindy.mythodea.api + +import jakarta.inject.Inject +import jakarta.ws.rs.GET +import jakarta.ws.rs.Path +import jakarta.ws.rs.Produces +import jakarta.ws.rs.QueryParam +import jakarta.ws.rs.core.MediaType +import java.time.Instant + +@Path("date/") +class MythodeaDateResource @Inject constructor() { + + @GET + @Path("from") + @Produces(MediaType.APPLICATION_JSON) + fun from(@QueryParam("epochMillis") epochMillis: Long): MitrasperanDate = + mitrasperan(if (epochMillis > 0) Instant.ofEpochMilli(epochMillis) else Instant.now()) +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000..135c32f --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1 @@ +quarkus.http.port=8080