diff --git a/aitools/Dockerfile b/aitools/Dockerfile
new file mode 100644
index 0000000..c10cdec
--- /dev/null
+++ b/aitools/Dockerfile
@@ -0,0 +1,29 @@
+# Stage 1: Build the application
+FROM maven:3.9.11-eclipse-temurin-25 AS build
+WORKDIR /app
+
+# Copy pom.xml and download dependencies
+COPY pom.xml .
+RUN mvn dependency:go-offline
+
+# Copy source code
+COPY src ./src
+
+# Build the application
+RUN mvn package -DskipTests
+
+# Stage 2: Create the runtime image
+FROM eclipse-temurin:25-jdk
+WORKDIR /app
+
+# Copy the built JAR file
+COPY --from=build /app/target/*.jar app.jar
+
+# Set environment variables
+ENV JAVA_OPTS=""
+
+# Expose the application port
+EXPOSE 8080
+
+# Run the application
+ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
\ No newline at end of file
diff --git a/aitools/pom.xml b/aitools/pom.xml
index 2ae4f67..9e2031b 100644
--- a/aitools/pom.xml
+++ b/aitools/pom.xml
@@ -40,11 +40,18 @@
lombok
true
+
org.springframework.boot
spring-boot-starter-webflux-test
test
+
+
+ org.springdoc
+ springdoc-openapi-starter-webflux-ui
+ 3.0.1
+
diff --git a/aitools/src/main/java/ru/ldeloff/aitools/datetime/controller/TimeController.java b/aitools/src/main/java/ru/ldeloff/aitools/datetime/controller/TimeController.java
index 4f56cd3..595a383 100644
--- a/aitools/src/main/java/ru/ldeloff/aitools/datetime/controller/TimeController.java
+++ b/aitools/src/main/java/ru/ldeloff/aitools/datetime/controller/TimeController.java
@@ -1,4 +1,40 @@
package ru.ldeloff.aitools.datetime.controller;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+import reactor.core.publisher.Mono;
+import java.time.ZoneId;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+
+@Slf4j
+@RestController
public class TimeController {
+
+ @GetMapping("/time/moscow")
+ @Operation(summary = "Get current Moscow time", description = "Returns current date and time in Moscow timezone (Europe/Moscow) as ISO 8601 string. Used as a tool for LLMs to get accurate local time.")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "Current time in Moscow in format yyyy-MM-dd HH:mm:ss"),
+ @ApiResponse(responseCode = "500", description = "Internal server error")
+ })
+ public Mono getMoscowTime() {
+ log.info("getMoscowTime");
+ return Mono.fromSupplier(() -> {
+ LocalDateTime moscowTime = LocalDateTime.now(ZoneId.of("Europe/Moscow"));
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+ return new TimeResponse(moscowTime.format(formatter));
+ });
+ }
+
+ public static class TimeResponse {
+ public final String time;
+
+ public TimeResponse(String time) {
+ this.time = time;
+ }
+ }
}
diff --git a/aitools/src/main/resources/application.properties b/aitools/src/main/resources/application.properties
index 061f835..fb3d424 100644
--- a/aitools/src/main/resources/application.properties
+++ b/aitools/src/main/resources/application.properties
@@ -1 +1,3 @@
spring.application.name=aitools
+
+springdoc.api-docs.path=/api-docs
diff --git a/cmd/deploy-common.bat b/cmd/deploy-common.bat
new file mode 100644
index 0000000..7f74ff4
--- /dev/null
+++ b/cmd/deploy-common.bat
@@ -0,0 +1,106 @@
+@echo off
+setlocal enabledelayedexpansion
+
+set SSH_SERVER_IP=192.168.10.110
+set SSH_SERVER_USER=root
+set SSH_CERT_PATH=%USERPROFILE%\.ssh\root_jarviceai
+set IMAGE_NAME=aitools-amd64:latest
+set ENV_FILE=.env
+set COMPOSE_FILE=docker-compose.yaml
+set PLATFORM=linux/amd64/v3
+
+
+REM === Общие настройки ===
+set DOCKERFILE=.\aitools\Dockerfile
+set PATH=.\aitools
+set IMAGE_TAR_NAME=image-aitools.tar
+set IMAGE_TAR_GZ_NAME=image-aitools.tar.gz
+
+REM === Проверка запущен ли Docker Daemon ===
+echo Checking if Docker is running...
+docker version >nul 2>&1
+if !errorlevel! neq 0 (
+ echo ERROR: Docker is not running or not installed!
+ echo Please start Docker Desktop and try again.
+ echo.
+ echo If Docker is installed, ensure it's running in the system tray.
+ echo You may need to restart Docker Desktop.
+ pause
+ exit /b 1
+)
+
+
+REM === Проверка наличия .env ===
+if not exist "%ENV_FILE%" (
+ echo Error: %ENV_FILE% not found!
+ pause
+ exit /b 1
+)
+
+
+REM === Собираем образы ===
+set TMP_ENV_ARGS=tmp_env_args.txt
+> "%TMP_ENV_ARGS%" (
+ for /f "usebackq tokens=*" %%a in ("%ENV_FILE%") do (
+ echo --build-arg %%a
+ )
+)
+set BUILD_ARGS=
+for /f "usebackq tokens=*" %%a in ("%TMP_ENV_ARGS%") do (
+ set BUILD_ARGS=!BUILD_ARGS! %%a
+)
+echo Building image locally with build args...
+docker build --platform "%PLATFORM%" -f "%DOCKERFILE%" -t "%IMAGE_NAME%" --progress=plain !BUILD_ARGS! "%PATH%"
+
+REM === Экспорт образов в tar ===
+echo Creating tar archives of images...
+docker save -o "%IMAGE_TAR_NAME%" "%IMAGE_NAME%"
+
+REM === Сжатие gzip (если доступно) ===
+where gzip >nul
+if !errorlevel! equ 0 (
+ echo Compressing images with gzip...
+ gzip "%IMAGE_TAR_NAME%"
+ set IMAGE=%IMAGE_TAR_GZ_NAME%
+) else (
+ echo Warning: gzip not found. Using uncompressed .tar files.
+ set IMAGE=%IMAGE_TAR_NAME%
+)
+
+REM === Передача на сервер ===
+echo Transferring images and files to server...
+scp -i "%SSH_CERT_PATH%" "%IMAGE%" "%SSH_SERVER_USER%@%SSH_SERVER_IP%:/tmp/"
+if errorlevel 1 (
+ echo Error: scp failed to upload %IMAGE%
+ pause
+ exit /b 1
+)
+
+scp -i "%SSH_CERT_PATH%" "%COMPOSE_FILE%" "%SSH_SERVER_USER%@%SSH_SERVER_IP%:/tmp/docker-compose.yaml"
+if errorlevel 1 (
+ echo Error: scp failed to upload %COMPOSE_FILE%
+ pause
+ exit /b 1
+)
+
+scp -i "%SSH_CERT_PATH%" "%ENV_FILE%" "%SSH_SERVER_USER%@%SSH_SERVER_IP%:/tmp/.env"
+if errorlevel 1 (
+ echo Error: scp failed to upload %ENV_FILE%
+ pause
+ exit /b 1
+)
+
+REM === Выполнение на сервере ===
+echo Creating directory on server and moving files...
+ssh -i "%SSH_CERT_PATH%" "%SSH_SERVER_USER%@%SSH_SERVER_IP%" "mkdir -p /opt/aitools && mv -f /tmp/docker-compose.yaml /opt/aitools/docker-compose.yaml && mv -f /tmp/.env /opt/aitools/.env && echo 'Files moved'"
+
+echo Loading images on server and starting...
+ssh -i "%SSH_CERT_PATH%" "%SSH_SERVER_USER%@%SSH_SERVER_IP%" "docker stop aitools 2>/dev/null || true && docker rm aitools 2>/dev/null || true && docker load < /tmp/%IMAGE% && rm /tmp/%IMAGE% && && cd /opt/aitools && docker compose up -d && echo 'Deployment completed!'"
+
+REM === Очистка локальных файлов ===
+echo Cleaning local archives...
+del /q "%IMAGE_TAR_NAME%" "%TMP_ENV_ARGS%" 2>nul
+del /q "%IMAGE_TAR_GZ_NAME%" 2>nul
+
+echo Deployment completed!
+pause
\ No newline at end of file
diff --git a/docker-compose.yaml b/docker-compose.yaml
new file mode 100644
index 0000000..4dd8a26
--- /dev/null
+++ b/docker-compose.yaml
@@ -0,0 +1,15 @@
+version: '3.8'
+
+services:
+ backend:
+ container_name: aitools
+ image: aitools-amd64:latest
+ env_file: .env
+ restart: unless-stopped
+ networks:
+ llm_tools:
+
+networks:
+ llm_tools:
+ name: llm_tools
+ driver: bridge