add projectService
This commit is contained in:
Generated
+11
-1
@@ -1,7 +1,17 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="CompilerConfiguration">
|
<component name="CompilerConfiguration">
|
||||||
<bytecodeTargetLevel target="17" />
|
<annotationProcessing>
|
||||||
|
<profile name="Gradle Imported" enabled="true">
|
||||||
|
<outputRelativeToContentRoot value="true" />
|
||||||
|
<processorPath useClasspath="false">
|
||||||
|
<entry name="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.projectlombok/lombok/1.18.34/ec547ef414ab1d2c040118fb9c1c265ada63af14/lombok-1.18.34.jar" />
|
||||||
|
</processorPath>
|
||||||
|
<module name="jambotron.main" />
|
||||||
|
<module name="jambotron.test" />
|
||||||
|
</profile>
|
||||||
|
</annotationProcessing>
|
||||||
|
<bytecodeTargetLevel target="21" />
|
||||||
</component>
|
</component>
|
||||||
<component name="JavacSettings">
|
<component name="JavacSettings">
|
||||||
<option name="ADDITIONAL_OPTIONS_STRING" value="-parameters" />
|
<option name="ADDITIONAL_OPTIONS_STRING" value="-parameters" />
|
||||||
|
|||||||
Generated
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="18" project-jdk-type="JavaSDK" />
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" project-jdk-name="18" project-jdk-type="JavaSDK" />
|
||||||
</project>
|
</project>
|
||||||
@@ -41,6 +41,9 @@ dependencies {
|
|||||||
implementation "org.flywaydb:flyway-database-postgresql:10.4.1"
|
implementation "org.flywaydb:flyway-database-postgresql:10.4.1"
|
||||||
implementation 'jakarta.persistence:jakarta.persistence-api:3.1.0'
|
implementation 'jakarta.persistence:jakarta.persistence-api:3.1.0'
|
||||||
|
|
||||||
|
// MongoDB
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-data-mongodb'
|
||||||
|
|
||||||
// Безопасность и JWT
|
// Безопасность и JWT
|
||||||
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
|
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
|
||||||
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
|
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
|
||||||
|
|||||||
@@ -20,6 +20,9 @@ services:
|
|||||||
jambotron-mongo:
|
jambotron-mongo:
|
||||||
image: mongo:6.0
|
image: mongo:6.0
|
||||||
container_name: jambotron-mongo
|
container_name: jambotron-mongo
|
||||||
|
environment:
|
||||||
|
- MONGO_INITDB_ROOT_USERNAME=root
|
||||||
|
- MONGO_INITDB_ROOT_PASSWORD=password
|
||||||
ports:
|
ports:
|
||||||
- "27017:27017"
|
- "27017:27017"
|
||||||
volumes:
|
volumes:
|
||||||
@@ -27,6 +30,18 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- jambotron-net
|
- jambotron-net
|
||||||
|
|
||||||
|
mongo-express:
|
||||||
|
image: mongo-express
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "8083:8081"
|
||||||
|
environment:
|
||||||
|
ME_CONFIG_MONGODB_URL: mongodb://root:password@mongodb:27017/
|
||||||
|
ME_CONFIG_BASICAUTH_USERNAME: admin
|
||||||
|
ME_CONFIG_BASICAUTH_PASSWORD: pass
|
||||||
|
networks:
|
||||||
|
- jambotron-net
|
||||||
|
|
||||||
# UI для управления БД (Postgres)[cite: 3]
|
# UI для управления БД (Postgres)[cite: 3]
|
||||||
jambo-pgadmin:
|
jambo-pgadmin:
|
||||||
image: dpage/pgadmin4
|
image: dpage/pgadmin4
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- jambotron-staging-net
|
- jambotron-staging-net
|
||||||
- jambotron-net # Для связи с Caddy
|
- jambotron-net # Для связи с Caddy
|
||||||
|
volumes:
|
||||||
|
- jambotron_data:/jambotron_data
|
||||||
environment:
|
environment:
|
||||||
SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE}
|
SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE}
|
||||||
SPRING_DATASOURCE_URL: jdbc:postgresql://jambo-db-staging:5432/${STAGING_DB_NAME}
|
SPRING_DATASOURCE_URL: jdbc:postgresql://jambo-db-staging:5432/${STAGING_DB_NAME}
|
||||||
@@ -43,3 +45,5 @@ networks:
|
|||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
jambo_db_staging_data:
|
jambo_db_staging_data:
|
||||||
|
jambotron_data:
|
||||||
|
name: jambotron_data
|
||||||
@@ -11,10 +11,14 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
import org.springframework.boot.CommandLineRunner;
|
import org.springframework.boot.CommandLineRunner;
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||||
|
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
|
||||||
|
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
|
@EnableJpaRepositories(basePackages = "com.jambotronGroup.jambotron.repository")
|
||||||
|
@EnableMongoRepositories(basePackages = "com.jambotronGroup.jambotron.repository")
|
||||||
public class JambotronApplication{
|
public class JambotronApplication{
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(JambotronApplication.class);
|
private static final Logger logger = LoggerFactory.getLogger(JambotronApplication.class);
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
package com.jambotronGroup.jambotron.controllers;
|
||||||
|
|
||||||
|
import com.jambotronGroup.jambotron.model.Project;
|
||||||
|
import com.jambotronGroup.jambotron.model.ProjectStatus;
|
||||||
|
import com.jambotronGroup.jambotron.projects.ProjectCreateRequest;
|
||||||
|
import com.jambotronGroup.jambotron.projects.ProjectService;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/v1/projects")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ProjectController {
|
||||||
|
|
||||||
|
private final ProjectService projectService;
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
public ResponseEntity<Project> create(@Valid @RequestBody ProjectCreateRequest request) {
|
||||||
|
Project newProject = projectService.createProject(request);
|
||||||
|
return ResponseEntity.status(HttpStatus.CREATED).body(newProject);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/{id}/deploy")
|
||||||
|
public ResponseEntity<String> deployProject(@PathVariable UUID id) {
|
||||||
|
// 1. Находим проект в БД
|
||||||
|
Project project = projectService.findById(id);
|
||||||
|
|
||||||
|
// 2. Вызываем магию деплоя
|
||||||
|
projectService.deployProject(project);
|
||||||
|
|
||||||
|
return ResponseEntity.ok("Deployment started for project: " + project.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/{id}/stop")
|
||||||
|
public ResponseEntity<String> stopProject(@PathVariable UUID id) {
|
||||||
|
// 1. Ищем проект в базе
|
||||||
|
Project project = projectService.findById(id);
|
||||||
|
|
||||||
|
// 2. Останавливаем контейнеры
|
||||||
|
projectService.stopProject(project);
|
||||||
|
|
||||||
|
// 3. (Опционально) Обновляем статус в базе
|
||||||
|
project.setStatus(ProjectStatus.STOPPED);
|
||||||
|
projectService.save(project); // Не забудь добавить метод save в сервис
|
||||||
|
|
||||||
|
return ResponseEntity.ok("Project stopped: " + project.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/{id}/config")
|
||||||
|
public ResponseEntity<String> updateConfig(
|
||||||
|
@PathVariable UUID id,
|
||||||
|
@RequestBody String newContent) {
|
||||||
|
|
||||||
|
// Обновляем данные в базе
|
||||||
|
projectService.updateProjectConfig(id, newContent);
|
||||||
|
|
||||||
|
return ResponseEntity.ok("Configuration updated. Re-deploy to apply changes.");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package com.jambotronGroup.jambotron.dockerClient;
|
||||||
|
|
||||||
|
import com.github.dockerjava.api.DockerClient;
|
||||||
|
import com.github.dockerjava.core.DefaultDockerClientConfig;
|
||||||
|
import com.github.dockerjava.core.DockerClientConfig;
|
||||||
|
import com.github.dockerjava.core.DockerClientImpl;
|
||||||
|
import com.github.dockerjava.httpclient5.ApacheDockerHttpClient;
|
||||||
|
import com.github.dockerjava.transport.DockerHttpClient;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class DockerConfig {
|
||||||
|
|
||||||
|
@Value("${docker.host}")
|
||||||
|
private String dockerHost;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public DockerClient dockerClient() {
|
||||||
|
DockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder()
|
||||||
|
.withDockerHost(dockerHost)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
DockerHttpClient httpClient = new ApacheDockerHttpClient.Builder()
|
||||||
|
.dockerHost(config.getDockerHost())
|
||||||
|
.sslConfig(config.getSSLConfig())
|
||||||
|
.maxConnections(100)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
return DockerClientImpl.getInstance(config, httpClient);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ public class GlobalConstants {
|
|||||||
public static Path PATH_ROOT = Path.of("/jambotron_data/");
|
public static Path PATH_ROOT = Path.of("/jambotron_data/");
|
||||||
public static Path PATH_PUBLIC = PATH_ROOT.resolve("public");
|
public static Path PATH_PUBLIC = PATH_ROOT.resolve("public");
|
||||||
public static Path PATH_USERS = PATH_ROOT.resolve("users");
|
public static Path PATH_USERS = PATH_ROOT.resolve("users");
|
||||||
|
public static Path PATH_PROJECTS = PATH_ROOT.resolve("projects"); // Добавили для структуры
|
||||||
public static Path PATH_INIT_DATA = PATH_ROOT.resolve("init_data");
|
public static Path PATH_INIT_DATA = PATH_ROOT.resolve("init_data");
|
||||||
public static Path PATH_JSON_DUMP = PATH_ROOT.resolve("json_dump");
|
public static Path PATH_JSON_DUMP = PATH_ROOT.resolve("json_dump");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package com.jambotronGroup.jambotron.model;
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "projects")
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class Project {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.UUID)
|
||||||
|
private UUID id;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Column(name = "owner_id", nullable = false)
|
||||||
|
private UUID ownerId;
|
||||||
|
|
||||||
|
@Column(name = "local_path")
|
||||||
|
private String localPath; // Путь к папке проекта на диске
|
||||||
|
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
@Column(length = 20)
|
||||||
|
private ProjectStatus status = ProjectStatus.CREATED;
|
||||||
|
|
||||||
|
@Column(name = "created_at", updatable = false)
|
||||||
|
private LocalDateTime createdAt = LocalDateTime.now();
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package com.jambotronGroup.jambotron.model;
|
||||||
|
|
||||||
|
import lombok.*;
|
||||||
|
import org.springframework.data.annotation.Id;
|
||||||
|
import org.springframework.data.mongodb.core.mapping.Document;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Document(collection = "project_configs")
|
||||||
|
@Data // Генерирует геттеры, сеттеры, toString, equals, hashCode
|
||||||
|
@Builder // Генерирует тот самый метод .builder()
|
||||||
|
@NoArgsConstructor // Нужен для десериализации MongoDB
|
||||||
|
@AllArgsConstructor // Нужен для работы @Builder
|
||||||
|
public class ProjectConfig {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
private String id; // Внутренний ID Mongo
|
||||||
|
|
||||||
|
private String projectId; // UUID из нашей таблицы projects
|
||||||
|
|
||||||
|
private String rawComposeContent; // Прямо текст твоего docker-compose.yaml
|
||||||
|
|
||||||
|
private Map<String, String> envVariables; // Ключ-значение для переменных
|
||||||
|
|
||||||
|
private Integer version = 1; // Для отслеживания правок конфига
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package com.jambotronGroup.jambotron.model;
|
||||||
|
|
||||||
|
public enum ProjectStatus {
|
||||||
|
CREATED, // Проект инициализирован в БД
|
||||||
|
DEPLOYING, // Запущен процесс docker-compose up
|
||||||
|
RUNNING, // Контейнеры активны
|
||||||
|
STOPPED, // Контейнеры остановлены
|
||||||
|
ERROR // Ошибка инфраструктуры
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package com.jambotronGroup.jambotron.projects;
|
||||||
|
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.messaging.handler.annotation.support.MethodArgumentNotValidException;
|
||||||
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
|
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@RestControllerAdvice
|
||||||
|
public class GlobalExceptionHandler {
|
||||||
|
|
||||||
|
@ExceptionHandler(ProjectAlreadyExistsException.class)
|
||||||
|
public ResponseEntity<String> handleConflict(ProjectAlreadyExistsException ex) {
|
||||||
|
return ResponseEntity.status(HttpStatus.CONFLICT).body(ex.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||||
|
public ResponseEntity<Map<String, String>> handleValidation(MethodArgumentNotValidException ex) {
|
||||||
|
Map<String, String> errors = new HashMap<>();
|
||||||
|
ex.getBindingResult().getFieldErrors().forEach(error ->
|
||||||
|
errors.put(error.getField(), error.getDefaultMessage()));
|
||||||
|
return ResponseEntity.badRequest().body(errors);
|
||||||
|
}
|
||||||
|
}
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
package com.jambotronGroup.jambotron.projects;
|
||||||
|
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||||
|
|
||||||
|
// Аннотация гарантирует, что если исключение не перехвачено Handler-ом,
|
||||||
|
// Spring всё равно вернет 409 статус.
|
||||||
|
@ResponseStatus(HttpStatus.CONFLICT)
|
||||||
|
public class ProjectAlreadyExistsException extends RuntimeException {
|
||||||
|
public ProjectAlreadyExistsException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.jambotronGroup.jambotron.projects;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import jakarta.validation.constraints.Size;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public record ProjectCreateRequest(
|
||||||
|
@NotBlank(message = "Имя проекта не может быть пустым")
|
||||||
|
@Size(min = 3, max = 50, message = "Имя проекта должно быть от 3 до 50 символов")
|
||||||
|
String name,
|
||||||
|
|
||||||
|
@NotNull(message = "ID владельца обязателен")
|
||||||
|
UUID ownerId
|
||||||
|
) {}
|
||||||
@@ -0,0 +1,160 @@
|
|||||||
|
package com.jambotronGroup.jambotron.projects;
|
||||||
|
|
||||||
|
import com.jambotronGroup.jambotron.globalConstants.GlobalConstants;
|
||||||
|
import com.jambotronGroup.jambotron.initData.InitDataService;
|
||||||
|
import com.jambotronGroup.jambotron.model.Project;
|
||||||
|
import com.jambotronGroup.jambotron.model.ProjectConfig;
|
||||||
|
import com.jambotronGroup.jambotron.repository.ProjectConfigRepository;
|
||||||
|
import com.jambotronGroup.jambotron.repository.ProjectRepository;
|
||||||
|
import jakarta.transaction.Transactional;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ProjectService {
|
||||||
|
|
||||||
|
private final ProjectRepository projectRepository;
|
||||||
|
|
||||||
|
private final ProjectConfigRepository configRepository;
|
||||||
|
|
||||||
|
private static final Logger _logger = LoggerFactory.getLogger(ProjectService.class);
|
||||||
|
|
||||||
|
public Project findById(UUID id) {
|
||||||
|
return projectRepository.findById(id)
|
||||||
|
.orElseThrow(() -> new RuntimeException("Project not found with id: " + id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public Project createProject(ProjectCreateRequest request) {
|
||||||
|
// 1. Проверка на уникальность имени внутри аккаунта пользователя
|
||||||
|
if (projectRepository.existsByNameAndOwnerId(request.name(), request.ownerId())) {
|
||||||
|
throw new ProjectAlreadyExistsException(
|
||||||
|
"Проект с именем '" + request.name() + "' уже существует у данного пользователя"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.info("Валидация пройдена. Создание проекта: {}", request.name());
|
||||||
|
|
||||||
|
Project project = new Project();
|
||||||
|
project.setName(request.name());
|
||||||
|
project.setOwnerId(request.ownerId());
|
||||||
|
project = projectRepository.save(project);
|
||||||
|
|
||||||
|
UUID projectId = project.getId();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 2. FS: Используем GlobalConstants[cite: 1]
|
||||||
|
Path projectDir = GlobalConstants.PATH_PROJECTS.resolve(projectId.toString());
|
||||||
|
Files.createDirectories(projectDir);
|
||||||
|
|
||||||
|
project.setLocalPath(projectDir.toString());
|
||||||
|
projectRepository.save(project);
|
||||||
|
|
||||||
|
// 3. MongoDB[cite: 1]
|
||||||
|
ProjectConfig config = ProjectConfig.builder()
|
||||||
|
.projectId(String.valueOf(project.getId()))
|
||||||
|
.rawComposeContent("version: '3.8'\nservices:\n app:\n image: nginx:alpine\n ports:\n - \"8080:80\"")
|
||||||
|
.build();
|
||||||
|
configRepository.save(config);
|
||||||
|
|
||||||
|
return project;
|
||||||
|
} catch (IOException e) {
|
||||||
|
_logger.error("FS Error: {}", projectId, e);
|
||||||
|
throw new RuntimeException("Ошибка инициализации хранилища");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deployProject(Project project) {
|
||||||
|
// 1. Получаем конфиг из Mongo
|
||||||
|
ProjectConfig config = configRepository.findByProjectId(String.valueOf(project.getId()))
|
||||||
|
.orElseThrow(() -> new RuntimeException("Config not found"));
|
||||||
|
|
||||||
|
// 2. Путь к папке проекта (уже создан при POST-запросе)
|
||||||
|
Path projectPath = Paths.get(project.getLocalPath());
|
||||||
|
File composeFile = projectPath.resolve("docker-compose.yml").toFile();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 3. Записываем YAML контент в файл
|
||||||
|
Files.writeString(composeFile.toPath(), config.getRawComposeContent());
|
||||||
|
_logger.info("Docker Compose file created at: {}", composeFile.getAbsolutePath());
|
||||||
|
|
||||||
|
// 4. Запускаем проект через ProcessBuilder (команда docker compose up)
|
||||||
|
runComposeUp(projectPath);
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException("Failed to deploy project files", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runComposeUp(Path projectPath) {
|
||||||
|
ProcessBuilder pb = new ProcessBuilder("docker", "compose", "up", "-d");
|
||||||
|
pb.directory(projectPath.toFile()); // Выполняем команду внутри папки проекта
|
||||||
|
pb.inheritIO(); // Перенаправляем логи докера в консоль Spring
|
||||||
|
|
||||||
|
try {
|
||||||
|
Process process = pb.start();
|
||||||
|
int exitCode = process.waitFor();
|
||||||
|
if (exitCode == 0) {
|
||||||
|
_logger.info("Project deployed successfully via Docker Compose!");
|
||||||
|
} else {
|
||||||
|
_logger.error("Docker Compose failed with exit code: {}", exitCode);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
_logger.error("Error executing Docker Compose", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stopProject(Project project) {
|
||||||
|
Path projectPath = Paths.get(project.getLocalPath());
|
||||||
|
|
||||||
|
_logger.info("Stopping project: {}", project.getId());
|
||||||
|
|
||||||
|
// ProcessBuilder для выполнения docker compose down
|
||||||
|
ProcessBuilder pb = new ProcessBuilder("docker", "compose", "down");
|
||||||
|
pb.directory(projectPath.toFile());
|
||||||
|
pb.inheritIO();
|
||||||
|
|
||||||
|
try {
|
||||||
|
Process process = pb.start();
|
||||||
|
int exitCode = process.waitFor();
|
||||||
|
|
||||||
|
if (exitCode == 0) {
|
||||||
|
_logger.info("Project {} stopped successfully.", project.getId());
|
||||||
|
} else {
|
||||||
|
_logger.error("Failed to stop project. Exit code: {}", exitCode);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
_logger.error("Error during docker compose down", e);
|
||||||
|
throw new RuntimeException("Could not stop docker project", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void save(Project project) {
|
||||||
|
projectRepository.save(project);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void updateProjectConfig(UUID projectId, String newYaml) {
|
||||||
|
// 1. Ищем конфиг в MongoDB
|
||||||
|
ProjectConfig config = configRepository.findByProjectId(String.valueOf(projectId))
|
||||||
|
.orElseThrow(() -> new RuntimeException("Config not found for project: " + projectId));
|
||||||
|
|
||||||
|
// 2. Обновляем поле с сырым YAML
|
||||||
|
config.setRawComposeContent(newYaml);
|
||||||
|
|
||||||
|
// 3. Сохраняем обратно в Mongo
|
||||||
|
configRepository.save(config);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package com.jambotronGroup.jambotron.repository;
|
||||||
|
|
||||||
|
import com.jambotronGroup.jambotron.model.ProjectConfig;
|
||||||
|
import org.springframework.data.mongodb.repository.MongoRepository;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface ProjectConfigRepository extends MongoRepository<ProjectConfig, String> {
|
||||||
|
|
||||||
|
// Нам часто нужно будет доставать конфиг именно по ID проекта из Postgres
|
||||||
|
Optional<ProjectConfig> findByProjectId(String projectId);
|
||||||
|
|
||||||
|
// Удаление конфига при удалении проекта
|
||||||
|
void deleteByProjectId(String projectId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.jambotronGroup.jambotron.repository;
|
||||||
|
|
||||||
|
import com.jambotronGroup.jambotron.model.Project;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface ProjectRepository extends JpaRepository<Project, UUID> {
|
||||||
|
|
||||||
|
// Поиск всех проектов конкретного пользователя
|
||||||
|
List<Project> findAllByOwnerId(UUID ownerId);
|
||||||
|
|
||||||
|
// Поиск по имени (например, для проверки уникальности внутри аккаунта)
|
||||||
|
boolean existsByNameAndOwnerId(String name, UUID ownerId);
|
||||||
|
}
|
||||||
@@ -117,6 +117,9 @@ public class WebSecurityConfig implements WebMvcConfigurer {// extends WebSecuri
|
|||||||
.requestMatchers("/api/public/zhipuai/image/**").permitAll()
|
.requestMatchers("/api/public/zhipuai/image/**").permitAll()
|
||||||
|
|
||||||
|
|
||||||
|
.requestMatchers("/api/v1/projects").permitAll()
|
||||||
|
.requestMatchers("/api/v1/projects/**").permitAll()
|
||||||
|
|
||||||
// Access permitted for specific roles
|
// Access permitted for specific roles
|
||||||
.requestMatchers("/api/user/**").hasRole("USER")
|
.requestMatchers("/api/user/**").hasRole("USER")
|
||||||
.requestMatchers("/api/user/file-browser-tree/**").hasRole("USER")
|
.requestMatchers("/api/user/file-browser-tree/**").hasRole("USER")
|
||||||
|
|||||||
@@ -5,16 +5,19 @@ spring.application.name=jambotron
|
|||||||
server.port=8082
|
server.port=8082
|
||||||
#============Localhost Configurations========================
|
#============Localhost Configurations========================
|
||||||
|
|
||||||
spring.datasource.url= jdbc:postgresql://localhost:5433/jambotronDB?options=-c%20timezone=UTC
|
spring.datasource.url= jdbc:postgresql://localhost:5432/jambotron_dev?options=-c%20timezone=UTC
|
||||||
spring.datasource.username= admin
|
spring.datasource.username= jambotron_admin
|
||||||
spring.datasource.password= postgrespw
|
spring.datasource.password= jambo_secret_pass
|
||||||
|
|
||||||
spring.flyway.baseline-on-migrate=true
|
spring.flyway.baseline-on-migrate=true
|
||||||
spring.flyway.validate-on-migrate=true
|
spring.flyway.validate-on-migrate=true
|
||||||
|
|
||||||
spring.flyway.url=jdbc:postgresql://localhost:5433/jambotronDB?options=-c%20timezone=UTC
|
spring.flyway.url=jdbc:postgresql://localhost:5432/jambotron_dev?options=-c%20timezone=UTC
|
||||||
spring.flyway.user=admin
|
spring.flyway.user=jambotron_admin
|
||||||
spring.flyway.password=postgrespw
|
spring.flyway.password=jambo_secret_pass
|
||||||
|
|
||||||
|
# ??? ???? ?????? Spring ?? ??????? mongoTemplate ?????????????
|
||||||
|
spring.data.mongodb.uri=mongodb://root:password@localhost:27017/jambotron?authSource=admin
|
||||||
|
|
||||||
#spring.datasource.url= jdbc:postgresql://localhost:5433/db_for_import
|
#spring.datasource.url= jdbc:postgresql://localhost:5433/db_for_import
|
||||||
#spring.datasource.username= admin
|
#spring.datasource.username= admin
|
||||||
@@ -57,4 +60,7 @@ spring.servlet.multipart.max-request-size=50MB
|
|||||||
|
|
||||||
|
|
||||||
server.tomcat.max-swallow-size=100MB
|
server.tomcat.max-swallow-size=100MB
|
||||||
#=============
|
#=============
|
||||||
|
|
||||||
|
|
||||||
|
docker.host=npipe:////./pipe/docker_engine
|
||||||
@@ -23,6 +23,9 @@ spring.flyway.url=${SPRING_FLYWAY_URL}
|
|||||||
spring.flyway.user=${SPRING_FLYWAY_USER}
|
spring.flyway.user=${SPRING_FLYWAY_USER}
|
||||||
spring.flyway.password=${SPRING_FLYWAY_PASSWORD}
|
spring.flyway.password=${SPRING_FLYWAY_PASSWORD}
|
||||||
|
|
||||||
|
# ??? ???? ?????? Spring ?? ??????? mongoTemplate ?????????????
|
||||||
|
spring.data.mongodb.uri=mongodb://root:password@localhost:27017/jambotron?authSource=admin
|
||||||
|
|
||||||
#============Koyeb Configurations========================
|
#============Koyeb Configurations========================
|
||||||
|
|
||||||
#spring.datasource.url= jdbc:postgresql://ep-red-breeze-a2cxhq5f.eu-central-1.pg.koyeb.app/jambotronDB
|
#spring.datasource.url= jdbc:postgresql://ep-red-breeze-a2cxhq5f.eu-central-1.pg.koyeb.app/jambotronDB
|
||||||
@@ -71,3 +74,4 @@ spring.servlet.multipart.max-request-size=50MB
|
|||||||
server.tomcat.max-swallow-size=100MB
|
server.tomcat.max-swallow-size=100MB
|
||||||
|
|
||||||
#=============
|
#=============
|
||||||
|
docker.host=unix:///var/run/docker.sock
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
CREATE TABLE projects (
|
||||||
|
id UUID PRIMARY KEY,
|
||||||
|
name VARCHAR(100) NOT NULL,
|
||||||
|
owner_id UUID NOT NULL,
|
||||||
|
local_path TEXT, -- Путь к папке проекта
|
||||||
|
status VARCHAR(20), -- CREATED, RUNNING, ERROR
|
||||||
|
created_at TIMESTAMP DEFAULT NOW()
|
||||||
|
);
|
||||||
Reference in New Issue
Block a user