Запуск init-скрипта при старте task-а
This commit is contained in:
parent
ec3f3318ac
commit
05c04c49ed
|
|
@ -2,6 +2,7 @@ package ru.oa2.lti;
|
|||
|
||||
import jakarta.transaction.Transactional;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.oa2.lti.infrastructure.runner.Runner;
|
||||
import ru.oa2.lti.model.LtiLogin;
|
||||
import ru.oa2.lti.model.TaskData;
|
||||
import ru.oa2.lti.repository.LMSContentRepository;
|
||||
|
|
@ -19,11 +20,14 @@ public class ApplicationService {
|
|||
|
||||
private final JwtService jwtService;
|
||||
private final LMSContentRepository lmsContentRepository;
|
||||
private final Runner runner;
|
||||
|
||||
public ApplicationService(JwtService jwtService,
|
||||
LMSContentRepository lmsContentRepository) {
|
||||
LMSContentRepository lmsContentRepository,
|
||||
Runner runner) {
|
||||
this.jwtService = jwtService;
|
||||
this.lmsContentRepository = lmsContentRepository;
|
||||
this.runner = runner;
|
||||
}
|
||||
|
||||
public Payload getPayload(LtiLogin ltiLogin) {
|
||||
|
|
@ -41,7 +45,10 @@ public class ApplicationService {
|
|||
//TODO добавить версию в Task и выбирать самую старшую и опубликованную
|
||||
TaskData data = tasks.stream().findFirst().get().getData();
|
||||
|
||||
//TODO запуск initScript
|
||||
if (!runner.run(contextId, data.getInitScript())) {
|
||||
return "Init script FAILED";
|
||||
}
|
||||
|
||||
return data.getDescription();
|
||||
} else {
|
||||
return "Not Page";
|
||||
|
|
@ -50,7 +57,6 @@ public class ApplicationService {
|
|||
|
||||
public String saveResult(String body) {
|
||||
|
||||
//TODO
|
||||
return "{\n" +
|
||||
" \"success\": true,\n" +
|
||||
" \"message\": \"Результат успешно получен и обработан\",\n" +
|
||||
|
|
|
|||
|
|
@ -1,31 +1,28 @@
|
|||
package ru.oa2.lti.controller;
|
||||
|
||||
import jakarta.servlet.ServletResponse;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||
import ru.oa2.lti.ApplicationService;
|
||||
import ru.oa2.lti.infrastructure.LMSService;
|
||||
import ru.oa2.lti.model.LtiLogin;
|
||||
import ru.oa2.lti.service.jwt.Payload;
|
||||
import ru.oa2.lti.service.jwt.TokenService;
|
||||
|
||||
@Slf4j
|
||||
@Controller
|
||||
@RequestMapping("/tool/lti")
|
||||
public class LoginController {
|
||||
public class LaunchController {
|
||||
|
||||
private final ApplicationService service;
|
||||
private final TokenService tokenService;
|
||||
private final LMSService lmsService;
|
||||
|
||||
public LoginController(ApplicationService applicationService,
|
||||
TokenService tokenService) {
|
||||
public LaunchController(ApplicationService applicationService,
|
||||
LMSService lmsService) {
|
||||
this.service = applicationService;
|
||||
this.tokenService = tokenService;
|
||||
this.lmsService = lmsService;
|
||||
}
|
||||
|
||||
@ResponseBody
|
||||
|
|
@ -36,9 +33,7 @@ public class LoginController {
|
|||
@RequestParam("lti_deployment_id") String ltiDeploymentId,
|
||||
@RequestParam("lti_message_hint") String ltiMessageHint,
|
||||
@RequestParam("target_link_uri") String targetLinkUri,
|
||||
HttpServletRequest request,
|
||||
RedirectAttributes redirectAttributes,
|
||||
ServletResponse servletResponse) {
|
||||
HttpServletRequest request) {
|
||||
|
||||
var ltiLogin = LtiLogin.builder()
|
||||
.clientId(clientId)
|
||||
|
|
@ -62,8 +57,7 @@ public class LoginController {
|
|||
session.setAttribute("payload", payload);
|
||||
|
||||
if (payload.getContextId() != null) {
|
||||
var body = tokenService.ltiAuth(ltiMessageHint, iss, loginHint);
|
||||
return body.getBody();
|
||||
return lmsService.ltiAuth(ltiMessageHint, iss, loginHint);
|
||||
}
|
||||
|
||||
return "redirect:/tool/lti/select";
|
||||
|
|
@ -85,8 +79,6 @@ public class LoginController {
|
|||
}
|
||||
|
||||
model.addAttribute("description", data);
|
||||
model.addAttribute("codeTitle", "Dockerfile:");
|
||||
model.addAttribute("inputCode", "FROM ...");
|
||||
}
|
||||
|
||||
return "task";
|
||||
|
|
@ -3,21 +3,17 @@ package ru.oa2.lti.controller;
|
|||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.autoconfigure.web.client.RestClientBuilderConfigurer;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.client.RestClient;
|
||||
import ru.oa2.lti.ApplicationService;
|
||||
import ru.oa2.lti.model.LtiLogin;
|
||||
import ru.oa2.lti.service.jwt.JwtService;
|
||||
import ru.oa2.lti.service.jwt.TokenService;
|
||||
import ru.oa2.lti.service.results.ResultService;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/tool/lti/result")
|
||||
|
|
@ -47,6 +43,7 @@ public class ResultController {
|
|||
HttpSession session = req.getSession(false);
|
||||
if (session == null) return ResponseEntity.status(401).build();
|
||||
|
||||
//TODO запуск скрипта проверки
|
||||
var ltiLogin = (LtiLogin) session.getAttribute("lti_login");
|
||||
var assessToken = tokenService.exchangeForAccessToken(
|
||||
ltiLogin.getClientId());
|
||||
|
|
|
|||
|
|
@ -1,5 +0,0 @@
|
|||
|
||||
```shell
|
||||
curl -XPOST http://localhost:9999/tool/lti/login \
|
||||
-d 'client_id=27e2d42d-d218-4ab9-b063-85e3ec87ec8f&iss=https%3A%2F%2Flocalhost&login_hint=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImU5ODA3YTg2LTVhNWQtNDI0MC1hYjI1LWNhYTUyZDliZjUxOSJ9.eyJkZXBsb3ltZW50S2V5IjoxLCJkZXBsb3ltZW50SWQiOiIyNzE1ZDA0NS03N2M1LTRkODgtYTFlNC05MmQ3MjFmY2M1NGIiLCJjb250ZXh0S2V5IjoxLCJjb250ZXh0SWQiOiJlMmE4ZGQxMy04MWJhLTQwMTEtODgwNi0wMjA5NGZkZjg5ZjMiLCJjb3Vyc2VhZG1pbiI6ZmFsc2UsImNvYWNoIjpmYWxzZSwicGFydGljaXBhbnQiOnRydWV9.vgPLHQA9scdED3_OwOy46h6VmzpN4arIHY3-YDBdhH4kuEqeCOjjtFbGdDqauKoQBTSVo4UvoK4JQLMhak6qsFchCj54mPob8jbaKLd0GnO_jY0sR609Nrk7Muq7cki_4PjVMX8TTHp-VYlSHjVxQH_z_D5Wld27J95z4qJjRU59GmvLGDqdLyerVVBO-zaavYsUbEEiAxoX3hmytxrarmJ7OHpxufNOeXzZ0DSGUmU5ycuTAqxODaHO1Y4rQM6XlvSfDh_TmXP8QEkatlp2cdjRpNWyOdUW_hZfbtkqukwt1ZP7KEgWzNI3vivpBjm2xfUG2YLwXPJqHa47NQgvsQ<i_deployment_id=2715d045-77c5-4d88-a1e4-92d721fcc54b<i_message_hint=1802240&target_link_uri=https%3A%2F%2Fopenolat.local%2Ftool'
|
||||
```
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package ru.oa2.lti.infrastructure;
|
||||
|
||||
public interface LMSService {
|
||||
|
||||
String ltiAuth(String ltiLoginHint, String iss, String loginHint);
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package ru.oa2.lti.infrastructure;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.client.RestClient;
|
||||
import ru.oa2.lti.config.AppProperties;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class LMSServiceImpl implements LMSService {
|
||||
|
||||
private final RestClient restClient;
|
||||
private final AppProperties appProperties;
|
||||
|
||||
public LMSServiceImpl(RestClient restClient, AppProperties appProperties) {
|
||||
this.restClient = restClient;
|
||||
this.appProperties = appProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String ltiAuth(String ltiLoginHint, String iss, String loginHint) {
|
||||
var result = restClient
|
||||
.get()
|
||||
.uri("/lti/auth?" +
|
||||
"state=start" +
|
||||
"&code=code1" +
|
||||
"&iss=" + iss +
|
||||
"&login_hint=" + loginHint +
|
||||
"&redirect_uri=" + appProperties.lmsUrl() + "/tool/lti/redirect" +
|
||||
"<i_message_hint=" + ltiLoginHint)
|
||||
.retrieve();
|
||||
|
||||
ResponseEntity<String> body = result.toEntity(String.class);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("lti/auth RESPONSE: {}", body);
|
||||
}
|
||||
return body.getBody();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package ru.oa2.lti.infrastructure.runner;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public interface Runner {
|
||||
|
||||
boolean run(UUID userId, String script);
|
||||
|
||||
boolean check(UUID userId, String script);
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
package ru.oa2.lti.infrastructure.runner;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.UUID;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class RunnerImpl implements Runner {
|
||||
|
||||
private final String TEMP_DIR = "./temp/";
|
||||
|
||||
@Override
|
||||
public boolean run(UUID userId, String script) {
|
||||
try {
|
||||
return runScript(userId, script);
|
||||
} catch (Exception ex) {
|
||||
log.error(ex.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//TODO добавить вывод результата/интерпретации
|
||||
@Override
|
||||
public boolean check(UUID userId, String script) {
|
||||
try {
|
||||
return runScript(userId, script);
|
||||
} catch (Exception ex) {
|
||||
log.error(ex.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean runScript(UUID userId, String script) throws Exception {
|
||||
|
||||
var processBuilder = new ProcessBuilder();
|
||||
processBuilder.command("bash", saveFile(userId, script));
|
||||
|
||||
var process = processBuilder.start();
|
||||
|
||||
BufferedReader reader = new BufferedReader(
|
||||
new InputStreamReader(process.getInputStream())
|
||||
);
|
||||
|
||||
StringBuilder output = new StringBuilder();
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
output.append(line).append("\n");
|
||||
}
|
||||
log.info(output.toString());
|
||||
|
||||
int exitCode = process.waitFor();
|
||||
return exitCode == 0;
|
||||
}
|
||||
|
||||
private String saveFile(UUID userId, String script) throws IOException {
|
||||
Path path = Paths.get(TEMP_DIR + userId.toString());
|
||||
|
||||
if (!Files.exists(path)) {
|
||||
Files.createDirectories(path);
|
||||
}
|
||||
var tm = System.currentTimeMillis();
|
||||
var scriptPath = Files.write(
|
||||
Paths.get(TEMP_DIR + userId + "/" + tm),
|
||||
script.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
return scriptPath.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -16,11 +16,9 @@ import java.util.Map;
|
|||
public class TokenService {
|
||||
|
||||
private final RestTemplate restTemplate = new RestTemplate();
|
||||
private final RestClient restClient;
|
||||
private final AppProperties appProperties;
|
||||
|
||||
public TokenService(RestClient restClient, AppProperties appProperties) {
|
||||
this.restClient = restClient;
|
||||
public TokenService(AppProperties appProperties) {
|
||||
this.appProperties = appProperties;
|
||||
}
|
||||
|
||||
|
|
@ -65,24 +63,4 @@ public class TokenService {
|
|||
throw new RuntimeException("Не удалось получить access token", e);
|
||||
}
|
||||
}
|
||||
|
||||
public ResponseEntity<String> ltiAuth(String ltiLoginHint, String iss, String loginHint) {
|
||||
var result = restClient
|
||||
.get()
|
||||
.uri("/lti/auth?" +
|
||||
"state=start" +
|
||||
"&code=code1" +
|
||||
"&iss=" + iss +
|
||||
"&login_hint=" + loginHint +
|
||||
"&redirect_uri=" + appProperties.lmsUrl() + "/tool/lti/redirect" +
|
||||
"<i_message_hint=" + ltiLoginHint)
|
||||
.retrieve();
|
||||
|
||||
ResponseEntity<String> body = result.toEntity(String.class);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("lti/auth RESPONSE: {}", body);
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package ru.oa2.lti.service.results.dto;
|
||||
|
||||
public record ResultResponse(
|
||||
String status
|
||||
) {
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
document.getElementById('submitBtn').addEventListener('click', function () {
|
||||
// Показываем, что загрузка началась
|
||||
const statusContainer = document.getElementById('statusContainer');
|
||||
const icon = document.getElementById('icon');
|
||||
const statusLabel = document.getElementById('statusLabel');
|
||||
|
||||
// Получаем данные пользователя
|
||||
const contextId = document.getElementById('contextId')?.value || 'unknown';
|
||||
const participantId = document.getElementById('participantId')?.value || 'unknown';
|
||||
|
||||
const payload = {
|
||||
contextId: contextId,
|
||||
participantId: participantId
|
||||
};
|
||||
|
||||
fetch('/tool/lti/result/docker', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload)
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) throw new Error(`Ошибка сети: ${response.status}`);
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
// Показываем статус
|
||||
statusContainer.style.display = 'flex';
|
||||
|
||||
if (data.status === 'success') {
|
||||
icon.className = 'fa-solid fa-check-circle status-success';
|
||||
statusLabel.textContent = 'Success';
|
||||
statusLabel.className = 'status-label status-success';
|
||||
} else {
|
||||
icon.className = 'fa-solid fa-xmark-circle status-failed';
|
||||
statusLabel.textContent = 'Failed';
|
||||
statusLabel.className = 'status-label status-failed';
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
statusContainer.style.display = 'flex';
|
||||
icon.className = 'fa-solid fa-xmark-circle status-failed';
|
||||
statusLabel.textContent = 'Failed';
|
||||
statusLabel.className = 'status-label status-failed';
|
||||
});
|
||||
});
|
||||
|
|
@ -4,182 +4,116 @@
|
|||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
|
||||
|
||||
|
||||
<style>
|
||||
body, html {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
font-family: Arial, sans-serif;
|
||||
}
|
||||
.container-fluid {
|
||||
height: 90vh;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0;
|
||||
}
|
||||
.row {
|
||||
height: 100%;
|
||||
}
|
||||
.left-section {
|
||||
border-right: 2px solid #dee2e6;
|
||||
padding: 20px;
|
||||
.content-section {
|
||||
flex: 1;
|
||||
padding: 30px 20px;
|
||||
overflow-y: auto;
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
.right-section {
|
||||
padding: 0;
|
||||
}
|
||||
.right-top {
|
||||
height: 50%;
|
||||
padding: 20px;
|
||||
border-bottom: 2px solid #dee2e6;
|
||||
}
|
||||
.right-bottom {
|
||||
height: 50%;
|
||||
padding: 20px;
|
||||
}
|
||||
.task-block {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
padding: 30px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
margin-bottom: 20px;
|
||||
box-shadow: 0 2px 6px rgba(0,0,0,0.1);
|
||||
line-height: 1.7;
|
||||
}
|
||||
.code-block {
|
||||
background: #f5f5f5;
|
||||
padding: 15px;
|
||||
border-radius: 5px;
|
||||
font-family: 'Courier New', monospace;
|
||||
margin: 10px 0;
|
||||
}
|
||||
.input-output {
|
||||
height: 100%;
|
||||
.footer {
|
||||
padding: 15px 20px;
|
||||
background-color: #f1f3f5;
|
||||
border-top: 1px solid #dee2e6;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.status-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: auto;
|
||||
margin-left: 15px;
|
||||
}
|
||||
.status-icon {
|
||||
font-size: 18px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
.status-success {
|
||||
color: #28a745;
|
||||
}
|
||||
.status-failed {
|
||||
color: #dc3545;
|
||||
}
|
||||
.status-label {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.submit-btn {
|
||||
padding: 10px 24px;
|
||||
font-size: 15px;
|
||||
}
|
||||
textarea {
|
||||
flex: 1;
|
||||
resize: none;
|
||||
font-family: 'Courier New', monospace;
|
||||
padding: 10px;
|
||||
}
|
||||
.output-area {
|
||||
background: #f8f9fa;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 5px;
|
||||
padding: 15px;
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
font-family: 'Courier New', monospace;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
.run-button {
|
||||
width: 100%;
|
||||
min-height: 200px;
|
||||
padding: 12px;
|
||||
margin-top: 15px;
|
||||
padding: 10px 30px;
|
||||
font-size: 16px;
|
||||
border: 1px solid #ced4da;
|
||||
border-radius: 6px;
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 14px;
|
||||
resize: none;
|
||||
}
|
||||
.result-title {
|
||||
margin-bottom: 10px;
|
||||
color: #495057;
|
||||
font-weight: bold;
|
||||
textarea:focus {
|
||||
outline: none;
|
||||
border-color: #80bdff;
|
||||
box-shadow: 0 0 0 0.2rem rgba(0,123,255,.25);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
|
||||
<!-- Левая секция -->
|
||||
<div class="col-md-6 left-section">
|
||||
<h3>Условие задачи</h3>
|
||||
<div class="task-block" th:utext="${description}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Правая секция -->
|
||||
<div class="col-md-6 right-section">
|
||||
<!-- Верхняя часть правой секции -->
|
||||
<div class="right-top">
|
||||
<h4>Введите решение</h4>
|
||||
<div class="input-output">
|
||||
<div class="code-prompt mb-2" th:utext="${codeTitle}">
|
||||
<!-- Текст подсказки для ввода кода -->
|
||||
</div>
|
||||
<textarea id="codeInput" class="form-control" th:text="${inputCode}">
|
||||
</textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Нижняя часть правой секции -->
|
||||
<div class="right-bottom">
|
||||
<h4>Результат выполнения</h4>
|
||||
<div class="input-output">
|
||||
<div class="result-title">Вывод программы:</div>
|
||||
<div id="outputArea" class="output-area">
|
||||
<!-- Здесь будет появляться результат -->
|
||||
Результат выполнения появится здесь...
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<button id="runButton" class="btn btn-primary run-button">
|
||||
<i class="fas fa-play"></i> Запустить
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Описание задачи — на всю ширину -->
|
||||
<div class="content-section">
|
||||
<div class="task-block" th:utext="${description}">
|
||||
<!-- Сюда вставляется HTML-описание задачи -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Футер: статус + кнопка -->
|
||||
<div class="footer">
|
||||
<!-- Статус (иконка + надпись) -->
|
||||
<div id="statusContainer" class="status-container" style="display: none;">
|
||||
<div id="statusIcon" class="status-icon">
|
||||
<i id="icon" class="fa-solid"></i>
|
||||
</div>
|
||||
<div id="statusLabel" class="status-label"></div>
|
||||
</div>
|
||||
|
||||
<!-- Кнопка отправки -->
|
||||
<button id="submitBtn" class="btn btn-primary submit-btn">
|
||||
<i class="fas fa-paper-plane"></i> Отправить на проверку
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.getElementById('runButton').addEventListener('click', function() {
|
||||
const codeInput = document.getElementById('codeInput').value;
|
||||
const outputArea = document.getElementById('outputArea');
|
||||
<!-- Скрытые поля (если нужно передать контекст) -->
|
||||
<input type="hidden" id="contextId" th:value="${contextId}" />
|
||||
<input type="hidden" id="participantId" th:value="${participantId}" />
|
||||
|
||||
// Показываем индикатор загрузки
|
||||
outputArea.innerHTML = '<div class="text-center"><i class="fas fa-spinner fa-spin"></i> Отправка данных...</div>';
|
||||
|
||||
// Получаем данные пользователя и контекста
|
||||
// Предположим, что эти значения хранятся в data-атрибутах или скрытых полях
|
||||
const contextId = document.getElementById('contextId')?.value || 'unknown_context';
|
||||
const participantId = document.getElementById('participantId')?.value || 'unknown_participant';
|
||||
|
||||
// Формируем JSON-объект для отправки
|
||||
const payload = {
|
||||
contextId: contextId,
|
||||
participantId: participantId,
|
||||
text: codeInput
|
||||
};
|
||||
|
||||
fetch('/tool/lti/result/docker', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(payload)
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error(`Ошибка сети: ${response.status}`);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
// Успешный ответ от сервера
|
||||
outputArea.innerHTML = '<div class="text-success">✓ Данные успешно отправлены!</div>';
|
||||
console.log('Ответ сервера:', data);
|
||||
})
|
||||
.catch(error => {
|
||||
// Обработка ошибок сети или сервера
|
||||
outputArea.innerHTML = `
|
||||
<div class="text-danger">
|
||||
<strong>Ошибка отправки:</strong><br>
|
||||
${error.message}
|
||||
</div>
|
||||
`;
|
||||
console.error('Ошибка при отправке:', error);
|
||||
});
|
||||
});
|
||||
|
||||
// Автоматический рост textarea при вводе
|
||||
document.getElementById('codeInput').addEventListener('input', function() {
|
||||
this.style.height = 'auto';
|
||||
this.style.height = (this.scrollHeight) + 'px';
|
||||
});
|
||||
</script>
|
||||
<script th:src="@{/tool/lti/js/main.js}"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package ru.oa2.lti.infrastructure;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import ru.oa2.lti.infrastructure.runner.Runner;
|
||||
import ru.oa2.lti.infrastructure.runner.RunnerImpl;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||
|
||||
|
||||
@SpringBootTest(classes = {RunnerImpl.class})
|
||||
public class RunnerTest {
|
||||
|
||||
@Autowired
|
||||
Runner runner;
|
||||
|
||||
@Test
|
||||
public void runnerTest() {
|
||||
|
||||
var script = "#!/bin/bash\n" +
|
||||
"echo \"Запуск развёртывания...\"\n" +
|
||||
"sleep 2\n" +
|
||||
"echo \"Развёртывание завершено.\"";
|
||||
|
||||
var result = runner.run(UUID.randomUUID(), script);
|
||||
|
||||
assertThat(result).isEqualTo(true);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue