Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ target
logs
attachments
*.patch

resources/secrets.yaml

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,4 @@
- https://habr.com/ru/articles/259055/

Список выполненных задач:
...
1-9, 11
21 changes: 11 additions & 10 deletions config/nginx.conf
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
# https://losst.ru/ustanovka-nginx-ubuntu-16-04
# https://pai-bx.com/wiki/nginx/2332-useful-redirects-in-nginx/#1
# sudo iptables -A INPUT ! -s 127.0.0.1 -p tcp -m tcp --dport 8080 -j DROP
events {
}

http {
server {
listen 80;
listen 80;

# https://www.digitalocean.com/community/tutorials/how-to-optimize-nginx-configuration
gzip on;
gzip on;
gzip_types text/css application/javascript application/json;
gzip_min_length 2048;

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
root /opt/jirarush/resources;

if ($request_uri ~ ';') {return 404;}
root /opt/jirarush/resources;

# proxy_cookie_flags ~ secure samesite=none;
if ($request_uri ~ ';') { return 404; }

# static
location /static/ {
expires 30d;
access_log off;
Expand All @@ -28,13 +28,14 @@ server {
location ~ (/$|/view/|/ui/|/oauth2/) {
expires 0m;
proxy_pass http://localhost:8080;
proxy_connect_timeout 30s;
proxy_connect_timeout 30s;
}
location ~ (/api/|/doc|/swagger-ui/|/v3/api-docs/) {
proxy_pass http://localhost:8080;
proxy_connect_timeout 150s;
proxy_connect_timeout 150s;
}
location / {
try_files /view/404.html = 404;
}
}
}
64 changes: 64 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
version: '3'

services:
postgres-db :
image : postgres
environment :
POSTGRES_USER : jira
POSTGRES_PASSWORD : JiraRush
ports :
- "5432:5432"
volumes :
- ./pgdata:/var/lib/postgresql/data
healthcheck :
test : [ "CMD-SHELL", "pg_isready -U postgres" ]
networks:
- mynetwork

postgres-db-test :
image : postgres
environment :
POSTGRES_USER : jira
POSTGRES_PASSWORD : JiraRush
ports :
- "5433:5432"
volumes :
- ./pgdata-test:/var/lib/postgresql/data
healthcheck :
test : [ "CMD-SHELL", "pg_isready -U postgres" ]
networks:
- mynetwork

myapp:
image: sha256:1893d8fe5a20
ports:
- "8080:8080"
extra_hosts :
- "host.docker.internal:host-gateway"
build :
context : .
dockerfile : Dockerfile
environment:
SPRING_DATASOURCE_URL: jdbc:postgresql://postgres-db:5432/jira
SPRING_DATASOURCE_USERNAME: jira
SPRING_DATASOURCE_PASSWORD: JiraRush
depends_on :
postgres-db :
condition : service_healthy
postgres-db-test :
condition : service_healthy
networks:
- mynetwork
nginx :
image : nginx
volumes :
- ./config/nginx.conf:/etc/nginx/nginx.conf
ports :
- "80:80"
depends_on :
- myapp
networks :
- mynetwork
networks:
mynetwork:
driver: bridge
5 changes: 5 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,11 @@
<artifactId>junit-platform-launcher</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
10 changes: 6 additions & 4 deletions resources/mails/email-confirmation.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
</head>
<body>
<p th:text="'Привет, ' + ${user.firstName} + '.'"/>
<p>Чтобы завершить настройку учетной записи и начать пользоваться JiraRush, подтвердите, что вы правильно указали вашу
электронную почту.</p>
<a th:href="${confirmationUrl}">Подтвердить почту</a>
<p th:text="#{hello} +', ' + ${user.firstName} + '.'"/>
<p th:text="#{email-confirmation.info_end_registration}"></p>
<a th:href="${confirmationUrl}">
<span th:text="#{email-confirmation.confirm_mail}"/>
</a>
<div th:insert="~{fragments :: locale}"></div>
</body>
</html>
9 changes: 5 additions & 4 deletions resources/mails/password-reset.html
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head th:fragment="head">
<title>JiraRush - установить новый пароль</title>
<title>JiraRush - set new password</title>
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
</head>
<body>
<p th:text="'Привет, ' + ${user.firstName} + '.'"/>
<p th:text="'Мы получили запрос на установку нового пароля JiraRush для учетной записи: ' + ${user.email} + '.'"/>
<a th:href="${resetUrl}">Установить пароль</a>
<p th:text="#{hello}+', ' + ${user.firstName} + '.'"/>
<p th:text="#{password-reset.infoPasswordRequest} + ': ' + ${user.email} + '.'"/>
<a th:href="${resetUrl}" th:text="#{password-reset.setPassword}">Set password</a>
<div th:insert="~{fragments :: locale}"></div>
</body>
</html>
21 changes: 21 additions & 0 deletions resources/view/fragments.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Fragment</title>
</head>
<body>
<div th:fragment="locale">
<form action="">
<label>
<select name="lang" onchange="this.form.submit">
<option></option>
<option value="en">English</option>
<option value="ua">Українська</option>
<option value="ru">Російська</option>
</select>
</label>
</form>
</div>
</body>
</html>
7 changes: 5 additions & 2 deletions resources/view/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@

<th:block th:replace="~{layout/main::page(title='JiraRush: mini bugtracking system',appMain=~{::appMain})}">
<appMain>
<h1>JiraRush Home page</h1>
<h1>
<span th:text="#{index.homePage}">JiraRush Home page</span>
</h1>
<div th:if="${authUser} != null">
<form action="/ui/logout" method="post">
<button class="btn btn-primary btn-lg mt-3" type="submit">Logout</button>
<button class="btn btn-primary btn-lg mt-3" type="submit" th:text="#{index.logout}">Logout</button>
</form>
<div th:insert="~{fragments :: locale}"></div>
</div>
</appMain>
</th:block>
8 changes: 0 additions & 8 deletions resources/view/login.html
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,6 @@ <h3 class="mb-3">Sign in</h3>
type="button">
<i class="fa-brands fa-google"></i>
</a>
<a class="btn btn-primary btn-lg me-2" href="/oauth2/authorization/vk" style="padding-left: 17px; padding-right: 17px;"
type="button">
<i class="fa-brands fa-vk"></i>
</a>
<a class="btn btn-danger btn-lg me-2" href="/oauth2/authorization/yandex" style="padding-left: 21px; padding-right: 21px;"
type="button">
<i class="fa-brands fa-yandex"></i>
</a>
<a class="btn btn-dark btn-lg me-2" href="/oauth2/authorization/github" type="button">
<i class="fa-brands fa-github"></i>
</a>
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/javarush/jira/JiraRushApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Profile;

@SpringBootApplication
@EnableConfigurationProperties(AppProperties.class)
@EnableCaching
public class JiraRushApplication {

public static void main(String[] args) {
SpringApplication.run(JiraRushApplication.class, args);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@
import org.springframework.core.io.UrlResource;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.*;
import java.nio.file.StandardOpenOption;

import static java.nio.file.Files.createDirectories;
import static java.nio.file.Files.createFile;

@UtilityClass
public class FileUtil {
Expand All @@ -24,15 +24,16 @@ public static void upload(MultipartFile multipartFile, String directoryPath, Str
if (multipartFile.isEmpty()) {
throw new IllegalRequestDataException("Select a file to upload.");
}

File dir = new File(directoryPath);
if (dir.exists() || dir.mkdirs()) {
File file = new File(directoryPath + fileName);
try (OutputStream outStream = new FileOutputStream(file)) {
var dir = Paths.get(directoryPath);
Path file = null;
try {
createDirectories(dir);
file = createFile(Path.of(fileName));
try (OutputStream outStream = Files.newOutputStream(file, StandardOpenOption.CREATE)) {
outStream.write(multipartFile.getBytes());
} catch (IOException ex) {
throw new IllegalRequestDataException("Failed to upload file" + multipartFile.getOriginalFilename());
}
} catch (IOException ex) {
throw new IllegalRequestDataException("Failed to upload file" + multipartFile.getOriginalFilename());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,4 +156,10 @@ public TaskTreeNode(TaskTo taskTo) {
this(taskTo, new LinkedList<>());
}
}

@PostMapping("/{id}/tags")
@ResponseStatus(HttpStatus.CREATED)
public void createTag(@PathVariable long id, @RequestParam String tag) {
taskService.createTag(id, tag);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
import com.javarush.jira.common.BaseRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;

Expand Down Expand Up @@ -37,4 +39,7 @@ WITH RECURSIVE task_with_subtasks AS (
WHERE id IN (SELECT child FROM task_with_subtasks)
""", nativeQuery = true)
void setTaskAndSubTasksSprint(long taskId, Long sprintId);
}

@Query("SELECT a.updated FROM Task t JOIN t.activities a WHERE a.statusCode = :statusCode")
LocalDateTime findByStatusCode(String statusCode);
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import org.springframework.util.Assert;

import java.time.LocalDateTime;
import java.time.Period;
import java.time.temporal.ChronoUnit;
import java.util.List;

import static com.javarush.jira.bugtracking.ObjectType.TASK;
Expand All @@ -39,6 +41,7 @@ public class TaskService {
private final SprintRepository sprintRepository;
private final TaskExtMapper extMapper;
private final UserBelongRepository userBelongRepository;
private final TaskRepository taskRepository;

@Transactional
public void changeStatus(long taskId, String statusCode) {
Expand Down Expand Up @@ -132,12 +135,28 @@ public void unAssign(long id, String userType, long userId) {
assignment.setEndpoint(LocalDateTime.now());
}

@Transactional
public void createTag(long id, String tag) {
var task = Util.checkExist(id, handler.getRepository().findFullById(id));
task.getTags().add(tag);
taskRepository.save(task);
}

@Transactional
public long timeInWork(long id) {
var task = Util.checkExist(id, handler.getRepository().findFullById(id));
var inProgress = taskRepository.findByStatusCode("in_progress");
var readyForReview = taskRepository.findByStatusCode("ready_for_review");
return ChronoUnit.HOURS.between(readyForReview, inProgress);
}

private void checkAssignmentActionPossible(long id, String userType, boolean assign) {
Assert.notNull(userType, "userType must not be null");
Task task = handler.getRepository().getExisted(id);
String possibleUserType = getRefTo(RefType.TASK_STATUS, task.getStatusCode()).getAux(1);
if (!userType.equals(possibleUserType)) {
throw new DataConflictException(String.format(assign ? CANNOT_ASSIGN : CANNOT_UN_ASSIGN, userType, task.getStatusCode()));

}
}
}
}
Loading