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
20 changes: 20 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Database
DB_URL=jdbc:postgresql://localhost:5432/jira
DB_USER_NAME=jira
DB_USER_PWD=JiraRush

# OAuth2 - GitHub
GITHUB_CLIENT_ID=3d0d8738e65881fff266
GITHUB_CLIENT_SECRET=0f97031ce6178bdf7b67a6af587f37e222a16120

# OAuth2 - Google
GOOGLE_CLIENT_ID=329113642700-f8if6pu68j2repq3ef6umd5jgiliup60.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=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

# OAuth2 - GitLab
GITLAB_CLIENT_ID=b8520a3266089063c0d8261cce36971defa513f5ffd9f9b7a3d16728fc83a494
GITLAB_CLIENT_SECRET=e72c65320cf9d6495984a37b0f9cc03ec46be0bb6f071feaebbfe75168117004

# Mail
MAIL_USER_NAME=jira4jr@gmail.com
MAIL_PASSWORD=zdfzsrqvgimldzyj
5 changes: 3 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ target
logs
attachments
*.patch


#.env
pgdata/
pgdata-test/
24 changes: 24 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Этап сборки
FROM maven:3.9.6-eclipse-temurin-17-alpine AS build

WORKDIR /app

COPY pom.xml .
RUN mvn dependency:go-offline

COPY src ./src
COPY resources ./resources
RUN mvn clean package -DskipTests

# Этап запуска
FROM eclipse-temurin:17-jre-alpine

WORKDIR /app

# Копируем JAR из этапа сборки
COPY --from=build /app/target/*.jar app.jar
COPY --from=build /app/resources ./resources

EXPOSE 8080

ENTRYPOINT ["java", "-jar", "app.jar"]
72 changes: 72 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,76 @@
- https://habr.com/ru/articles/259055/

Список выполненных задач:
## Команды загрузки
```bash
docker run -p 5432:5432 --name postgres-db -e POSTGRES_USER=jira -e POSTGRES_PASSWORD=JiraRush -e POSTGRES_DB=jira -e PGDATA=/var/lib/postgresql/data/pgdata -v $(pwd)/pgdata:/var/lib/postgresql/data -d postgres
```
```bash
docker run -p 5433:5432 --name postgres-db-test -e POSTGRES_USER=jira -e POSTGRES_PASSWORD=JiraRush -e POSTGRES_DB=jira-test -e PGDATA=/var/lib/postgresql/data/pgdata -v $(pwd)/pgdata-test:/var/lib/postgresql/data -d postgres
```

1. Разобраться со структурой проекта (onboarding).✅

2. Удалить социальные сети: vk, yandex. `Easy task`✅

3. Вынести чувствительную информацию в отдельный проперти файл: ✅
- логин
- пароль БД
- идентификаторы для OAuth регистрации/авторизации
- настройки почты

Значения этих проперти должны считываться при старте сервера из переменных окружения машины. `Easy task`
- ```
добавлен .env файл в проект
написан тест
проект учебный .env не добавлен в gitignore
```

4. Переделать тесты так, чтоб во время тестов использовалась **in memory БД (H2)**, а не PostgreSQL. Для этого нужно определить 2 бина, и выборка какой из них использовать должно определяться активным профилем Spring. H2 не поддерживает все фичи, которые есть у PostgreSQL, поэтому тебе прийдется немного упростить скрипты с тестовыми данными.

5. Написать тесты для всех публичных методов контроллера `ProfileRestController`. Хоть методов только 2, но тестовых методов должно быть больше, т.к. нужно проверить success and unsuccess path. ✅

6. Сделать рефакторинг метода `com.javarush.jira.bugtracking.attachment.FileUtil#upload` чтоб он использовал современный подход для работы с файловой системмой. `Easy task` ✅

7. Добавить новый функционал: добавления тегов к задаче (REST API + реализация на сервисе). Фронт делать необязательно. Таблица `task_tag` уже создана.

8. Добавить подсчет времени сколько задача находилась в работе и тестировании. Написать 2 метода на уровне сервиса, которые параметром принимают задачу и возвращают затраченное время: ✅
- Сколько задача находилась в работе (ready_for_review минус in_progress).
- Сколько задача находилась на тестировании (done минус ready_for_review).

Для написания этого задания, нужно добавить в конец скрипта инициализации базы данных `changelog.sql` **3 записи** в таблицу `ACTIVITY`:
```
insert into ACTIVITY ( ID, AUTHOR_ID, TASK_ID, UPDATED, STATUS_CODE ) values ...
```
Со статусами:
- время начала работы над задачей – **in_progress**
- время окончания разработки - **ready_for_review**
- время конца тестирования - **done**

9. Написать `Dockerfile` для основного сервера ✅
```
добавлен Dockerfile и docker-compose.yml
docker build --no-cache --progress=plain -t project-final-app .

```
```bash
docker build --no-cache --progress=plain -t project-final-app .
```
```bash
docker-compose up -d
```

10. Написать `docker-compose` файл для запуска контейнера сервера вместе с БД и [nginx](https://nginx.org/ru/docs/). Для nginx используй конфиг-файл `config/nginx.conf`. При необходимости файл конфига можно редактировать. `Hard task` ✅
```bash
docker-compose down -v
```
```bash
docker-compose up -d --build
```
```bash

```
11. Добавить локализацию минимум на двух языках для шаблонов писем (mails) и стартовой страницы `index.html`.

12. Переделать механизм распознавания «свой-чужой» между фронтом и беком с `JSESSIONID` на `JWT`. Из сложностей – тебе придётся переделать отправку форм с фронта, чтоб добавлять хедер аутентификации. `Extra-hard task`
...
88 changes: 53 additions & 35 deletions config/nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,57 @@
# 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
server {
listen 80;

# https://www.digitalocean.com/community/tutorials/how-to-optimize-nginx-configuration
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;}

# proxy_cookie_flags ~ secure samesite=none;

# static
location /static/ {
expires 30d;
access_log off;
}
location /robots.txt {
access_log off;
}

location ~ (/$|/view/|/ui/|/oauth2/) {
expires 0m;
proxy_pass http://localhost:8080;
proxy_connect_timeout 30s;
}
location ~ (/api/|/doc|/swagger-ui/|/v3/api-docs/) {
proxy_pass http://localhost:8080;
proxy_connect_timeout 150s;
}
location / {
try_files /view/404.html = 404;
}
listen 80;

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

location /static/ {
expires 30d;
access_log off;
}

location /robots.txt {
access_log off;
}

# API endpoints - должны идти первыми
location /api/ {
proxy_pass http://app:8080/api/;
proxy_connect_timeout 150s;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}

location /actuator/ {
proxy_pass http://app:8080/actuator/;
proxy_connect_timeout 150s;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}

# UI и основные страницы
location ~ (/$|/view/|/ui/|/oauth2/) {
expires 0m;
proxy_pass http://app:8080;
proxy_connect_timeout 30s;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}

location / {
try_files /view/404.html = 404;
}
}
55 changes: 55 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
version: '3.8'

services:
postgres:
image: postgres:15-alpine
container_name: jira_postgres
environment:
POSTGRES_DB: jira
POSTGRES_USER: ${DB_USER_NAME}
POSTGRES_PASSWORD: ${DB_USER_PWD}
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- jira_network
restart: unless-stopped

app:
build: .
container_name: jira_app
env_file:
- .env
environment:
DB_URL: jdbc:postgresql://postgres:5432/jira
depends_on:
- postgres
networks:
- jira_network
restart: unless-stopped

nginx:
image: nginx:alpine
container_name: jira_nginx
ports:
- "80:80"
volumes:
# Монтируем наш конфиг в sites-available
- ./config/nginx.conf:/etc/nginx/sites-available/jirarush.conf:ro
# Создаём симлинк в sites-enabled
- ./config/nginx.conf:/etc/nginx/sites-enabled/jirarush.conf:ro
# Монтируем статику
- ./resources:/opt/jirarush/resources:ro
depends_on:
- app
networks:
- jira_network
restart: unless-stopped

volumes:
postgres_data:

networks:
jira_network:
driver: bridge
6 changes: 6 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,12 @@
<artifactId>junit-platform-launcher</artifactId>
<scope>test</scope>
</dependency>
<!-- для работы с .env-->
<dependency>
<groupId>me.paulschwarz</groupId>
<artifactId>spring-dotenv</artifactId>
<version>4.0.0</version>
</dependency>
</dependencies>

<build>
Expand Down
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
8 changes: 0 additions & 8 deletions resources/view/unauth/register.html
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,6 @@ <h3 class="mb-3">Registration</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
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,8 @@
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;
Expand All @@ -25,14 +23,15 @@ public static void upload(MultipartFile multipartFile, String directoryPath, Str
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)) {
outStream.write(multipartFile.getBytes());
} catch (IOException ex) {
throw new IllegalRequestDataException("Failed to upload file" + multipartFile.getOriginalFilename());
try {
Path dir = Paths.get(directoryPath);
if (!Files.exists(dir)) {
Files.createDirectories(dir);
}
Path file = dir.resolve(fileName);
Files.write(file, multipartFile.getBytes());
} catch (IOException e) {
throw new IllegalRequestDataException("Failed to upload file" + multipartFile.getOriginalFilename());
}
}

Expand Down
Loading