Initial Commit
Some checks failed
CI / deploy (push) Failing after 5s

This commit is contained in:
Arindy 2024-10-22 20:53:00 +02:00
commit 8fe7a2c427
17 changed files with 767 additions and 0 deletions

26
.github/workflows/ci.yaml vendored Normal file
View File

@ -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"

9
.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
*~
searxng-docker.service
caddy
srv
searxng/uwsgi.ini
.idea
target

1
.mvn/wrapper/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
maven-wrapper.jar

View File

@ -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);
}
}
}

20
.mvn/wrapper/maven-wrapper.properties vendored Normal file
View File

@ -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

11
compose.yml Normal file
View File

@ -0,0 +1,11 @@
services:
api:
container_name: ${REPO}
image: ${REPO}
restart: always
networks:
- reverse_proxy
networks:
reverse_proxy:
external: true

206
pom.xml Normal file
View File

@ -0,0 +1,206 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>de.arindy</groupId>
<artifactId>mythodea-api</artifactId>
<version>1.0.0-SNAPSHOT</version>
<properties>
<compiler-plugin.version>3.13.0</compiler-plugin.version>
<kotlin.version>2.0.21</kotlin.version>
<maven.compiler.release>21</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
<quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
<quarkus.platform.version>3.15.1</quarkus.platform.version>
<skipITs>true</skipITs>
<surefire-plugin.version>3.3.1</surefire-plugin.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>${quarkus.platform.group-id}</groupId>
<artifactId>${quarkus.platform.artifact-id}</artifactId>
<version>${quarkus.platform.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-jackson</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-kotlin</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>kotlin-extensions</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test</artifactId>
<version>${kotlin.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<sourceDirectory>src/main/kotlin</sourceDirectory>
<testSourceDirectory>src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<groupId>${quarkus.platform.group-id}</groupId>
<artifactId>quarkus-maven-plugin</artifactId>
<version>${quarkus.platform.version}</version>
<extensions>true</extensions>
<executions>
<execution>
<goals>
<goal>build</goal>
<goal>generate-code</goal>
<goal>generate-code-tests</goal>
<goal>native-image-agent</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>${compiler-plugin.version}</version>
<configuration>
<parameters>true</parameters>
</configuration>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>${surefire-plugin.version}</version>
<configuration>
<systemPropertyVariables>
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
<maven.home>${maven.home}</maven.home>
</systemPropertyVariables>
</configuration>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>${surefire-plugin.version}</version>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
<configuration>
<systemPropertyVariables>
<native.image.path>${project.build.directory}/${project.build.finalName}-runner
</native.image.path>
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
<maven.home>${maven.home}</maven.home>
</systemPropertyVariables>
</configuration>
</plugin>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
<configuration>
<sourceDirs>
<source>src/main/kotlin</source>
<source>target/generated-sources/annotations</source>
</sourceDirs>
</configuration>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
<configuration>
<sourceDirs>
<source>src/test/kotlin</source>
<source>target/generated-test-sources/test-annotations</source>
</sourceDirs>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-allopen</artifactId>
<version>${kotlin.version}</version>
</dependency>
</dependencies>
<configuration>
<javaParameters>true</javaParameters>
<jvmTarget>1.8</jvmTarget>
<compilerPlugins>
<plugin>all-open</plugin>
</compilerPlugins>
<pluginOptions>
<option>all-open:annotation=jakarta.ws.rs.Path</option>
<option>all-open:annotation=jakarta.enterprise.context.ApplicationScoped</option>
<option>all-open:annotation=jakarta.persistence.Entity</option>
<option>all-open:annotation=io.quarkus.test.junit.QuarkusTest</option>
</pluginOptions>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>native</id>
<activation>
<property>
<name>native</name>
</property>
</activation>
<properties>
<skipITs>false</skipITs>
<quarkus.native.enabled>true</quarkus.native.enabled>
</properties>
</profile>
</profiles>
</project>

View File

@ -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"]

View File

@ -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"]

View File

@ -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 GreetingResource @Inject constructor(var repository: Repository) {
@GET
@Path("from")
@Produces(MediaType.APPLICATION_JSON)
fun from(@QueryParam("epochMillis") epochMillis: Long): MitrasperanDate =
mitrasperan(if (epochMillis > 0) Instant.ofEpochMilli(epochMillis) else Instant.now())
}

View File

@ -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)
}
}

View File

@ -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)
}

View File

@ -0,0 +1,10 @@
package de.arindy.mythodea.api
import jakarta.enterprise.context.RequestScoped
@RequestScoped
class RepoImpl: Repository {
override fun name(): String {
return "Hello from Quarkus REST"
}
}

View File

@ -0,0 +1,6 @@
package de.arindy.mythodea.api
interface Repository {
fun name(): String
}

View File

@ -0,0 +1 @@
quarkus.http.port=8080

View File

@ -0,0 +1,8 @@
package de.arindy.mythodea.api
import io.quarkus.test.junit.QuarkusIntegrationTest
@QuarkusIntegrationTest
class GreetingResourceIT : GreetingResourceTest() {
}

View File

@ -0,0 +1,9 @@
package de.arindy.mythodea.api
import io.quarkus.test.junit.QuarkusTest
@QuarkusTest
class GreetingResourceTest {
}