forked from Alexey/DAS_2024_1
Compare commits
53 Commits
balakhonov
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 85b809333b | |||
| 5e3c9c0d5b | |||
| daf24d364d | |||
| 6c13deb231 | |||
| 543d41d9c3 | |||
| 153684c403 | |||
| 0708b01560 | |||
| 8a6932ff20 | |||
| 35cf16824d | |||
| ac3dc2e566 | |||
| 2f46c05849 | |||
| 84cb26162c | |||
| 129b991712 | |||
| ffecef8fa3 | |||
| 1289d67a62 | |||
| b09f3ea844 | |||
| 2f368ffb07 | |||
| ead06782ad | |||
| b2ac5eba9a | |||
| 0c0a47549a | |||
|
|
84e8cac198 | ||
| bde242318f | |||
| 761cc83ebd | |||
| 4699fda797 | |||
| 940cc6757f | |||
| 1e9bdf2806 | |||
|
|
9bd14a60b4 | ||
| 5aa2cae670 | |||
| 281d30a89e | |||
| f2093f376c | |||
|
|
eeac04be49 | ||
| 0a73e2d5d4 | |||
| 1565e49462 | |||
| 858ea65e71 | |||
| 0f898b968d | |||
| 8b96102dbd | |||
| 82ecad71f4 | |||
| ef68b506b8 | |||
|
|
8efc2422cf | ||
| c3537b5abe | |||
| a0ef65e0f9 | |||
| 6815b2e560 | |||
| 48b7fbd900 | |||
| 080625d270 | |||
| d9f5f75f5e | |||
| 7d9c9ec4d0 | |||
| 6ce78e60ad | |||
| b13182c80e | |||
| 822467bd99 | |||
| d7faf2a1b7 | |||
| d98803227e | |||
| 6f12270c73 | |||
| 6e6266c228 |
14
bazunov_andrew_lab_2/FileCreator/Dockerfile
Normal file
14
bazunov_andrew_lab_2/FileCreator/Dockerfile
Normal file
@@ -0,0 +1,14 @@
|
||||
# Используем официальный образ Go в качестве базового
|
||||
FROM golang:1.23
|
||||
|
||||
# Устанавливаем рабочую директорию
|
||||
WORKDIR /app
|
||||
|
||||
# Копируем файлы модуля
|
||||
COPY . .
|
||||
|
||||
# Сборка модуля
|
||||
RUN go build -o /bin/FileCreator
|
||||
|
||||
# Запуск модуля
|
||||
CMD ["/bin/FileCreator"]
|
||||
1
bazunov_andrew_lab_2/FileCreator/go.mod
Normal file
1
bazunov_andrew_lab_2/FileCreator/go.mod
Normal file
@@ -0,0 +1 @@
|
||||
module FileCreator
|
||||
92
bazunov_andrew_lab_2/FileCreator/main.go
Normal file
92
bazunov_andrew_lab_2/FileCreator/main.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
const DIR = "/var/data"
|
||||
|
||||
func Exists(name string) (bool, error) {
|
||||
_, err := os.Stat(name)
|
||||
if os.IsNotExist(err) {
|
||||
return false, nil
|
||||
}
|
||||
return err != nil, err
|
||||
}
|
||||
|
||||
func CreateDirectory(dirs string) error {
|
||||
if _, err := os.Stat(dirs); os.IsNotExist(err) {
|
||||
err := os.MkdirAll(dirs, 0664)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func CreateFileOrOpenIfExist(name string) (*os.File, error) {
|
||||
err := CreateDirectory(filepath.Dir(name))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
exists, err := Exists(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if exists {
|
||||
return os.OpenFile(name, os.O_WRONLY|os.O_CREATE, 0664)
|
||||
}
|
||||
return os.Create(name)
|
||||
}
|
||||
|
||||
func CreateFileAndWriteData(filename string) error {
|
||||
file, err := CreateFileOrOpenIfExist(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lines := rand.Intn(1000) + 100
|
||||
|
||||
for i := 0; i < lines; i++ {
|
||||
randomValueForLine := rand.Intn(1_000_000)
|
||||
_, err = fmt.Fprintf(file, "%d\r\n", randomValueForLine)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = file.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetMD5Hash(text string) string {
|
||||
hash := md5.Sum([]byte(text))
|
||||
return hex.EncodeToString(hash[:])
|
||||
}
|
||||
|
||||
func main() {
|
||||
for i := 0; i < 10; i++ {
|
||||
filename := fmt.Sprintf("%s/%s.txt", DIR, GetMD5Hash(fmt.Sprintf("%d", i)))
|
||||
err := CreateFileAndWriteData(filename)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
} else {
|
||||
fmt.Printf("Created file %s\n", filename)
|
||||
}
|
||||
}
|
||||
|
||||
err := CreateFileAndWriteData(DIR + "/data.txt")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
} else {
|
||||
fmt.Printf("Created file %s\n", DIR+"/data.txt")
|
||||
}
|
||||
}
|
||||
14
bazunov_andrew_lab_2/FirstService/Dockerfile
Normal file
14
bazunov_andrew_lab_2/FirstService/Dockerfile
Normal file
@@ -0,0 +1,14 @@
|
||||
# Используем официальный образ Go в качестве базового
|
||||
FROM golang:1.23
|
||||
|
||||
# Устанавливаем рабочую директорию
|
||||
WORKDIR /app
|
||||
|
||||
# Копируем файлы модуля
|
||||
COPY . .
|
||||
|
||||
# Сборка модуля
|
||||
RUN go build -o /bin/FirstService
|
||||
|
||||
# Запуск модуля
|
||||
CMD ["/bin/FirstService"]
|
||||
1
bazunov_andrew_lab_2/FirstService/go.mod
Normal file
1
bazunov_andrew_lab_2/FirstService/go.mod
Normal file
@@ -0,0 +1 @@
|
||||
module RVIP2
|
||||
94
bazunov_andrew_lab_2/FirstService/main.go
Normal file
94
bazunov_andrew_lab_2/FirstService/main.go
Normal file
@@ -0,0 +1,94 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
// Формирует файл /var/result/data.txt так,
|
||||
// что каждая строка файла - количество символов в именах файлов из каталога /var/data.
|
||||
|
||||
const INPUT = "/var/data"
|
||||
const OUTPUT = "/data/result"
|
||||
|
||||
func GetListFilesInDirectory(directory string) ([]string, error) {
|
||||
f, err := os.Open(directory)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return nil, err
|
||||
}
|
||||
files, err := f.Readdir(0)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var fileNames []string
|
||||
for _, file := range files {
|
||||
fileName := file.Name()
|
||||
fileNames = append(fileNames, fileName)
|
||||
}
|
||||
return fileNames, nil
|
||||
}
|
||||
|
||||
func Exists(name string) (bool, error) {
|
||||
_, err := os.Stat(name)
|
||||
if os.IsNotExist(err) {
|
||||
return false, nil
|
||||
}
|
||||
return err != nil, err
|
||||
}
|
||||
|
||||
func CreateFileOrOpenIfExist(name string) (*os.File, error) {
|
||||
exists, err := Exists(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if exists {
|
||||
return os.OpenFile(name, os.O_WRONLY|os.O_CREATE, 0664)
|
||||
}
|
||||
return os.Create(name)
|
||||
}
|
||||
|
||||
func CreateFileAndWriteData(filename string, lines []string) error {
|
||||
file, err := CreateFileOrOpenIfExist(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, line := range lines {
|
||||
_, err = fmt.Fprintf(file, line)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = file.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
filenames, err := GetListFilesInDirectory(INPUT)
|
||||
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
var lenghtOfFilenames []string
|
||||
for _, filename := range filenames {
|
||||
fmt.Println(filename)
|
||||
lenghtOfFilenames = append(lenghtOfFilenames, fmt.Sprintf("%d", len(filename)))
|
||||
}
|
||||
|
||||
err = CreateFileAndWriteData(OUTPUT+"/data.txt", filenames)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("First Service is end.")
|
||||
}
|
||||
30
bazunov_andrew_lab_2/README.md
Normal file
30
bazunov_andrew_lab_2/README.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# Распределенные вычисления и приложения Л2
|
||||
## _Автор Базунов Андрей Игревич ПИбд-42_
|
||||
|
||||
Сервисы ( _порядок исполнения сервисов соблюден_ ):
|
||||
- 1.FileCreator - (_Создание тестовых данных_)
|
||||
- 2.FirstService - (_Выполнение 1.4 варианта задания_)
|
||||
- 3.SecondService - (_Выполнение 2.2 варианта задания_)
|
||||
|
||||
В качестве основного языка был выбран GoLang. Для каждого сервиса был создан DOCKERFILE где были прописаны условия и действия для сборки каждого из модулей
|
||||
|
||||
# Docker
|
||||
|
||||
>Перед исполнением вполняем установку docker и проверяем версию
|
||||
|
||||
```sh
|
||||
docker-compose --version
|
||||
```
|
||||
|
||||
>Далее производим настройку файла docker-compose.yaml и запускаем контейнер с сборкой образов
|
||||
|
||||
```sh
|
||||
docker-compose up -d --build
|
||||
```
|
||||
|
||||
>Для завершения работы контейнера используем команду
|
||||
```sh
|
||||
docker-compose down
|
||||
```
|
||||
|
||||
[Демонстрация работы](https://vk.com/video236673313_456239575)
|
||||
14
bazunov_andrew_lab_2/SecondService/Dockerfile
Normal file
14
bazunov_andrew_lab_2/SecondService/Dockerfile
Normal file
@@ -0,0 +1,14 @@
|
||||
# Используем официальный образ Go в качестве базового
|
||||
FROM golang:1.23
|
||||
|
||||
# Устанавливаем рабочую директорию
|
||||
WORKDIR /app
|
||||
|
||||
# Копируем файлы модуля
|
||||
COPY . .
|
||||
|
||||
# Сборка модуля
|
||||
RUN go build -o /bin/SecondService
|
||||
|
||||
# Запуск модуля
|
||||
CMD ["/bin/SecondService"]
|
||||
1
bazunov_andrew_lab_2/SecondService/go.mod
Normal file
1
bazunov_andrew_lab_2/SecondService/go.mod
Normal file
@@ -0,0 +1 @@
|
||||
module SecondService
|
||||
79
bazunov_andrew_lab_2/SecondService/main.go
Normal file
79
bazunov_andrew_lab_2/SecondService/main.go
Normal file
@@ -0,0 +1,79 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
//Ищет наименьшее число из файла /var/data/data.txt и сохраняет его третью степень в /var/result/result.txt.
|
||||
|
||||
const INPUT = "/var/data/data.txt"
|
||||
const OUTPUT = "/var/result/result.txt"
|
||||
|
||||
func ReadlinesFromFile(filename string) ([]string, error) {
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var output []string
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
output = append(output, scanner.Text())
|
||||
}
|
||||
|
||||
err = file.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return output, nil
|
||||
}
|
||||
|
||||
func WriteIntToFile(filename string, i int) error {
|
||||
file, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func(file *os.File) {
|
||||
err := file.Close()
|
||||
if err != nil {
|
||||
|
||||
}
|
||||
}(file)
|
||||
|
||||
_, err = file.WriteString(fmt.Sprintf("%d\n", i))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
lines, err := ReadlinesFromFile(INPUT)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
minValue := 0
|
||||
|
||||
for _, line := range lines {
|
||||
if intValue, err := fmt.Sscanf(line, "%d", &minValue); err != nil {
|
||||
fmt.Println(err)
|
||||
} else {
|
||||
if minValue >= intValue {
|
||||
minValue = intValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err = WriteIntToFile(OUTPUT, minValue); err != nil {
|
||||
return
|
||||
} else {
|
||||
fmt.Printf("Write %d to %s\n", minValue, OUTPUT)
|
||||
}
|
||||
|
||||
fmt.Println("Second Service is end.")
|
||||
}
|
||||
27
bazunov_andrew_lab_2/docker-compose.yaml
Normal file
27
bazunov_andrew_lab_2/docker-compose.yaml
Normal file
@@ -0,0 +1,27 @@
|
||||
services:
|
||||
file_generate:
|
||||
build:
|
||||
context: ./FileCreator
|
||||
dockerfile: Dockerfile
|
||||
volumes:
|
||||
- ./data:/var/data # Монтирование локальной папки data в /var/data в контейнере
|
||||
|
||||
first_service:
|
||||
build:
|
||||
context: ./FirstService
|
||||
dockerfile: Dockerfile
|
||||
volumes:
|
||||
- ./data:/var/data
|
||||
- ./data:/var/result
|
||||
depends_on:
|
||||
- file_generate
|
||||
|
||||
second_service:
|
||||
build:
|
||||
context: ./SecondService
|
||||
dockerfile: Dockerfile
|
||||
volumes:
|
||||
- ./data:/var/data
|
||||
- ./data:/var/result
|
||||
depends_on:
|
||||
- first_service
|
||||
35
bogdanov_dmitry_lab_1/README.md
Normal file
35
bogdanov_dmitry_lab_1/README.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Лабораторная работа №1
|
||||
|
||||
## Богданов Дмитрий ПИбд-42
|
||||
|
||||
### Для выполнения были развернуты следующие сервисы:
|
||||
|
||||
* PostgreSQL - база данных
|
||||
* Mediawiki - движок вики
|
||||
* Gitea - движок гита
|
||||
|
||||
### С использованием следующих технологий:
|
||||
|
||||
* git
|
||||
* docker
|
||||
* docker-compose
|
||||
|
||||
|
||||
### Запуск лабораторной:
|
||||
Необходимо перейти в папку с файлом docker-compose.yaml и ввести следующую команду:
|
||||
```
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
## Результат запуска:
|
||||
```
|
||||
[+] Running 4/4
|
||||
✔ Network bogdanov_dmitry_lab_1_default Created 0.0s
|
||||
✔ Container bogdanov_dmitry_lab_1-mediawiki-1 Started 0.7s
|
||||
✔ Container bogdanov_dmitry_lab_1-git-1 Started 0.8s
|
||||
✔ Container bogdanov_dmitry_lab_1-db-1 Started 0.7s
|
||||
```
|
||||
|
||||
## Видео с результатом запуска:
|
||||
|
||||
Видео можно посмотреть по данной [ссылке](https://drive.google.com/file/d/1TES58HIeCnnKbtwWgED2oig4N7plBmol/view).
|
||||
40
bogdanov_dmitry_lab_1/docker-compose.yaml
Normal file
40
bogdanov_dmitry_lab_1/docker-compose.yaml
Normal file
@@ -0,0 +1,40 @@
|
||||
services:
|
||||
# PostgreSQL
|
||||
db:
|
||||
# Образ контейнера
|
||||
image: postgres
|
||||
# Перезапуск при падении
|
||||
restart: always
|
||||
# Порт для подключения
|
||||
ports:
|
||||
- 5432:5432
|
||||
# Каталог с данными. Каталог компьютера:каталог контейнера
|
||||
volumes:
|
||||
- ./volumes/postgres:/var/lib/postgresql/data
|
||||
# Переменные среды для определения хотя бы одного пользователя при запуске
|
||||
environment:
|
||||
- POSTGRES_USER=admin
|
||||
- POSTGRES_PASSWORD=admin
|
||||
# Mediawiki
|
||||
mediawiki:
|
||||
# Образ
|
||||
image: mediawiki
|
||||
# Перезапуск при падении
|
||||
restart: always
|
||||
# Порт для подключения
|
||||
ports:
|
||||
- 8080:80
|
||||
# Каталоги
|
||||
volumes:
|
||||
- ./volumes/mediawiki:/var/www/html/images
|
||||
# Gitea
|
||||
git:
|
||||
image: gitea/gitea:latest
|
||||
restart: always
|
||||
ports:
|
||||
- "3000:3000"
|
||||
- "222:22"
|
||||
volumes:
|
||||
- ./volumes/gitea:/data
|
||||
- ./volumes/timezone:/etc/timezone:ro
|
||||
- ./volumes/localtime:/etc/localtime:ro
|
||||
58
borschevskaya_anna_lab_3/README.md
Normal file
58
borschevskaya_anna_lab_3/README.md
Normal file
@@ -0,0 +1,58 @@
|
||||
# Отчет. Лабораторная работа 3
|
||||
|
||||
## Описание
|
||||
В рамках лабораторной работы № 3 были реализованы два сервиса (Java + Spring), осуществляющие CRUD-операции над сущностями.
|
||||
|
||||
Модель данных следующая:
|
||||
|
||||
Сущность "Компания" (сервис company)
|
||||
- идентификатор компании
|
||||
- название
|
||||
- адрес
|
||||
|
||||
Сущность "Вакансия" (сервис vacancy)
|
||||
- идентификатор вакансии
|
||||
- название
|
||||
- описание
|
||||
- нижняя граница зарплаты
|
||||
- верхняя граница зарплаты
|
||||
- идентификатор компании
|
||||
|
||||
Компания с вакансией связана как "один ко многим".
|
||||
|
||||
Каждый из сервисов имеет API с пятью эндпоинтами. При этом в сервисе vacancy при запросе вакансии по id происходит
|
||||
дополнительный запрос в сервис company для получения информации по компании, связанной с вакансией.
|
||||
Происходит это взаимодействие с помощью библиотеки OpenFeign, которая "под капотом" использует HttpClient.
|
||||
|
||||
В качестве хранилища данных использовалась СУБД Postgres. У каждого сервиса своя база данных в поднятой СУБД.
|
||||
Для создания схемы БД была использована библиотека Flyway, которая применила написанные миграции при старте приложения.
|
||||
|
||||
Запросы к сервисам проксирует шлюз на основе Nginx. Для этого перед запуском nginx был описан конфигурационный файл nginx.conf,
|
||||
в котором описан прослушиваемый порт и название сервера (в блоке server), маршруты до микросервисов и параметры проксирования (в блоке location).
|
||||
|
||||
Таким образом, с помощью Docker Compose были подняты сервисы:
|
||||
- company
|
||||
- vacancy
|
||||
- postgres
|
||||
- nginx
|
||||
## Как запустить
|
||||
Для того, чтобы запустить сервисы, необходимо выполнить следующие действия:
|
||||
1. Установить и запустить Docker Engine или Docker Desktop
|
||||
2. Через консоль перейти в папку, в которой расположен файл docker-compose.yml
|
||||
3. Выполнить команду:
|
||||
```
|
||||
docker compose up --build
|
||||
```
|
||||
В случае успешного запуска всех контейнеров в консоли будет выведено следующее сообщение:
|
||||
```
|
||||
[+] Running 5/5
|
||||
✔ Network borschevskaya_anna_lab_3_default Created 0.0s
|
||||
✔ Container postgres Started 0.6s
|
||||
✔ Container vacancy Started 1.1s
|
||||
✔ Container company Started 0.9s
|
||||
✔ Container borschevskaya_anna_lab_3-nginx-1 Started
|
||||
```
|
||||
Далее можно осуществлять запросы к сервисам по адресу http://localhost/{location}, где часть пути location меняется в зависимости от сервиса и запроса.
|
||||
## Видео-отчет
|
||||
Работоспособность лабораторной работы можно оценить в следующем [видео](https://disk.yandex.ru/i/KPNBfnlcgl1auw).
|
||||
Демонстрация взаимодействия с системой производится с применением сервиса Postman.
|
||||
38
borschevskaya_anna_lab_3/company-service/.gitignore
vendored
Normal file
38
borschevskaya_anna_lab_3/company-service/.gitignore
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea/modules.xml
|
||||
.idea/jarRepositories.xml
|
||||
.idea/compiler.xml
|
||||
.idea/libraries/
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### Eclipse ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
||||
### Mac OS ###
|
||||
.DS_Store
|
||||
21
borschevskaya_anna_lab_3/company-service/Dockerfile
Normal file
21
borschevskaya_anna_lab_3/company-service/Dockerfile
Normal file
@@ -0,0 +1,21 @@
|
||||
# Используем образ Maven для сборки
|
||||
FROM maven:3.8-eclipse-temurin-21-alpine AS build
|
||||
|
||||
# Устанавливаем рабочую директорию
|
||||
WORKDIR /app
|
||||
|
||||
# Копируем остальные исходные файлы
|
||||
COPY pom.xml .
|
||||
COPY src src
|
||||
|
||||
# Собираем весь проект
|
||||
RUN mvn clean package -DskipTests
|
||||
|
||||
# Используем официальный образ JDK для запуска собранного jar-файла
|
||||
FROM eclipse-temurin:21-jdk-alpine
|
||||
|
||||
# Копируем jar-файл из предыдущего этапа
|
||||
COPY --from=build /app/target/*.jar /app.jar
|
||||
|
||||
# Указываем команду для запуска приложения
|
||||
CMD ["java", "-jar", "app.jar"]
|
||||
66
borschevskaya_anna_lab_3/company-service/pom.xml
Normal file
66
borschevskaya_anna_lab_3/company-service/pom.xml
Normal file
@@ -0,0 +1,66 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>ru.somecompany</groupId>
|
||||
<artifactId>company-service</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.2.3</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.flywaydb</groupId>
|
||||
<artifactId>flyway-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.30</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.swagger.core.v3</groupId>
|
||||
<artifactId>swagger-annotations</artifactId>
|
||||
<version>2.2.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||
<version>2.3.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,12 @@
|
||||
package ru.somecompany;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class CompanyApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(CompanyApplication.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package ru.somecompany.controller;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import ru.somecompany.domain.CompanyEntity;
|
||||
import ru.somecompany.domain.CreateCompanyRequest;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
|
||||
@Validated
|
||||
@Tag(name = "company", description = "API для управления компаниями")
|
||||
public interface CompanyController {
|
||||
|
||||
@Operation(summary = "Получение всех компаний")
|
||||
@GetMapping(value = "/api/v1/company")
|
||||
List<CompanyEntity> getCompanies();
|
||||
|
||||
@Operation(summary = "Создание компании")
|
||||
@PostMapping(value = "/api/v1/company")
|
||||
CompanyEntity createCompany(@RequestBody @NotNull CreateCompanyRequest companyRequest);
|
||||
|
||||
@Operation(summary = "Получение информации о компании по id")
|
||||
@GetMapping(value = "/api/v1/company/{companyId}")
|
||||
CompanyEntity getCompany(@PathVariable UUID companyId);
|
||||
|
||||
@Operation(summary = "Редактирование компании")
|
||||
@PutMapping(value = "/api/v1/company/{companyId}")
|
||||
CompanyEntity updateCompany(@PathVariable UUID companyId,
|
||||
@RequestBody @NotNull CreateCompanyRequest companyRequest);
|
||||
|
||||
@Operation(summary = "Удалении компании по id")
|
||||
@DeleteMapping(value = "/api/v1/company/{companyId}")
|
||||
void deleteCompany(@PathVariable UUID companyId);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package ru.somecompany.controller;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import ru.somecompany.domain.CompanyEntity;
|
||||
import ru.somecompany.domain.CreateCompanyRequest;
|
||||
import ru.somecompany.service.CompanyService;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
public class CompanyControllerImpl implements CompanyController {
|
||||
|
||||
private final CompanyService companyService;
|
||||
|
||||
@Override
|
||||
public List<CompanyEntity> getCompanies() {
|
||||
return companyService.getCompanies();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompanyEntity createCompany(CreateCompanyRequest companyRequest) {
|
||||
return companyService.createCompany(companyRequest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompanyEntity getCompany(UUID companyId) {
|
||||
return companyService.getCompany(companyId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompanyEntity updateCompany(UUID companyId, CreateCompanyRequest companyRequest) {
|
||||
return companyService.updateCompany(companyId, companyRequest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteCompany(UUID companyId) {
|
||||
companyService.deleteCompany(companyId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package ru.somecompany.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Data
|
||||
@Entity
|
||||
@Builder
|
||||
@Table(name = "company")
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class CompanyEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private UUID id;
|
||||
|
||||
@Column(name = "name", unique = true, nullable = false)
|
||||
private String name;
|
||||
|
||||
@Column(name = "address", unique = true, nullable = false)
|
||||
private String address;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package ru.somecompany.domain;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class CreateCompanyRequest {
|
||||
|
||||
private String name;
|
||||
|
||||
private String address;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package ru.somecompany.exception;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static org.springframework.http.HttpStatus.NOT_FOUND;
|
||||
|
||||
@Slf4j
|
||||
public abstract class AbstractWebExceptionHandler {
|
||||
|
||||
public static ResponseEntity<ResponseError> buildResponse(ErrorCode errorCode, HttpStatus status, String msg,
|
||||
boolean details, Throwable ex) {
|
||||
return buildResponse(errorCode.name(), status, msg, details, ex, LocalDateTime.now());
|
||||
}
|
||||
|
||||
public static ResponseEntity<ResponseError> buildResponse(String errorCode, HttpStatus status, String msg,
|
||||
boolean details, Throwable ex, LocalDateTime timestamp) {
|
||||
if (details) {
|
||||
log.error(msg, ex); // with stack-trace
|
||||
} else {
|
||||
var message = StringUtils.equals(msg, ex.getMessage()) ? msg : msg + ": " + ex.getMessage();
|
||||
if (status == NOT_FOUND) {
|
||||
log.warn(message);
|
||||
} else {
|
||||
log.error(message);
|
||||
}
|
||||
}
|
||||
|
||||
return ResponseEntity.status(status.value())
|
||||
.body(ResponseError.builder()
|
||||
.code(errorCode)
|
||||
.message(msg)
|
||||
.timestamp(timestamp)
|
||||
.status(status.value())
|
||||
.build());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package ru.somecompany.exception;
|
||||
|
||||
public enum BasicError implements ErrorCode {
|
||||
NOT_FOUND
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package ru.somecompany.exception;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
@Getter
|
||||
public class CompanyAppRuntimeException extends RuntimeException {
|
||||
private final ErrorCode errorCode;
|
||||
private final HttpStatus status;
|
||||
private final boolean details;
|
||||
|
||||
public CompanyAppRuntimeException(ErrorCode errorCode, HttpStatus status, String message) {
|
||||
this(errorCode, status, message, null);
|
||||
}
|
||||
|
||||
public CompanyAppRuntimeException(ErrorCode errorCode, HttpStatus status, String message, Throwable error) {
|
||||
this(errorCode, status, message, true, error);
|
||||
}
|
||||
|
||||
public CompanyAppRuntimeException(ErrorCode errorCode, HttpStatus status, String message, boolean details, Throwable error) {
|
||||
super(message, error);
|
||||
this.errorCode = errorCode;
|
||||
this.status = status;
|
||||
this.details = details;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package ru.somecompany.exception;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public interface ErrorCode extends Serializable {
|
||||
|
||||
String name();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package ru.somecompany.exception;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
import static ru.somecompany.exception.BasicError.NOT_FOUND;
|
||||
|
||||
public class ResourceNotFoundException extends CompanyAppRuntimeException {
|
||||
|
||||
public ResourceNotFoundException(ErrorCode errorCode, String message) {
|
||||
this(errorCode, message, null);
|
||||
}
|
||||
|
||||
public ResourceNotFoundException(ErrorCode errorCode, String message, Throwable error) {
|
||||
super(errorCode, HttpStatus.NOT_FOUND, message, error);
|
||||
}
|
||||
|
||||
public static ResourceNotFoundException notFound(String message, Object... args) {
|
||||
return new ResourceNotFoundException(NOT_FOUND, String.format(message, args), null);
|
||||
}
|
||||
|
||||
public static ResourceNotFoundException notFound() {
|
||||
return new ResourceNotFoundException(NOT_FOUND, "Запрашиваемые данные не найдены", null);
|
||||
}
|
||||
|
||||
public static ResourceNotFoundException notFound(Throwable error) {
|
||||
return new ResourceNotFoundException(NOT_FOUND, error.getMessage(), error);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package ru.somecompany.exception;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class ResponseError {
|
||||
@JsonProperty(required = true)
|
||||
private LocalDateTime timestamp;
|
||||
@JsonProperty(required = true)
|
||||
private Integer status;
|
||||
@JsonProperty(required = true)
|
||||
private String message;
|
||||
@JsonProperty(required = true)
|
||||
private String code;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package ru.somecompany.exception;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
@Slf4j
|
||||
@Getter
|
||||
@RestControllerAdvice
|
||||
@RequiredArgsConstructor
|
||||
public class WebExceptionHandler extends AbstractWebExceptionHandler {
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
@ExceptionHandler(CompanyAppRuntimeException.class)
|
||||
public ResponseEntity<ResponseError> handleException(CompanyAppRuntimeException ex) {
|
||||
return buildResponse(ex.getErrorCode(), ex.getStatus(), ex.getMessage(), ex.isDetails(), ex);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package ru.somecompany.repository;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import ru.somecompany.domain.CompanyEntity;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public interface CompanyRepository extends JpaRepository<CompanyEntity, UUID> {
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package ru.somecompany.service;
|
||||
|
||||
import ru.somecompany.domain.CompanyEntity;
|
||||
import ru.somecompany.domain.CreateCompanyRequest;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface CompanyService {
|
||||
|
||||
List<CompanyEntity> getCompanies();
|
||||
|
||||
CompanyEntity createCompany(CreateCompanyRequest companyRequest);
|
||||
|
||||
CompanyEntity updateCompany(UUID companyId, CreateCompanyRequest companyRequest);
|
||||
|
||||
void deleteCompany(UUID companyId);
|
||||
|
||||
CompanyEntity getCompany(UUID companyId);
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package ru.somecompany.service;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.somecompany.domain.CompanyEntity;
|
||||
import ru.somecompany.domain.CreateCompanyRequest;
|
||||
import ru.somecompany.repository.CompanyRepository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import static ru.somecompany.exception.ResourceNotFoundException.notFound;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class CompanyServiceImpl implements CompanyService {
|
||||
|
||||
private final CompanyRepository companyRepository;
|
||||
|
||||
@Override
|
||||
public List<CompanyEntity> getCompanies() {
|
||||
return companyRepository.findAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompanyEntity createCompany(CreateCompanyRequest companyRequest) {
|
||||
var entity = CompanyEntity.builder()
|
||||
.name(companyRequest.getName())
|
||||
.address(companyRequest.getAddress())
|
||||
.build();
|
||||
return companyRepository.save(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompanyEntity updateCompany(UUID companyId, CreateCompanyRequest companyRequest) {
|
||||
var company = getById(companyId);
|
||||
company.setName(companyRequest.getName());
|
||||
company.setAddress(companyRequest.getAddress());
|
||||
return companyRepository.save(company);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteCompany(UUID companyId) {
|
||||
var company = getById(companyId);
|
||||
companyRepository.delete(company);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompanyEntity getCompany(UUID companyId) {
|
||||
return getById(companyId);
|
||||
}
|
||||
|
||||
private CompanyEntity getById(UUID companyId) {
|
||||
return companyRepository.findById(companyId)
|
||||
.orElseThrow(() -> notFound("Компания не найдена"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
server:
|
||||
port: ${SERVER_PORT:8080}
|
||||
spring:
|
||||
application:
|
||||
name: company-app
|
||||
jpa:
|
||||
database: POSTGRESQL
|
||||
open-in-view: false
|
||||
show-sql: false
|
||||
hibernate:
|
||||
ddl-auto: none
|
||||
properties:
|
||||
hibernate:
|
||||
dialect: org.hibernate.dialect.PostgreSQLDialect
|
||||
datasource:
|
||||
url: ${DB_URL:jdbc:postgresql://postgres:5433/company}
|
||||
username: ${DB_USERNAME:postgres}
|
||||
password: ${DB_PASSWORD:postgres}
|
||||
driverClassName: org.postgresql.Driver
|
||||
type: com.zaxxer.hikari.HikariDataSource
|
||||
hikari:
|
||||
maximum-pool-size: 15
|
||||
minimum-idle: 4
|
||||
idle-timeout: 180000
|
||||
max-lifetime: 599000
|
||||
flyway:
|
||||
enabled: true
|
||||
locations: db/migration/
|
||||
@@ -0,0 +1,6 @@
|
||||
CREATE TABLE company (
|
||||
id UUID
|
||||
CONSTRAINT company_pk PRIMARY KEY,
|
||||
name VARCHAR(255) UNIQUE NOT NULL,
|
||||
address VARCHAR(255) NOT NULL
|
||||
);
|
||||
43
borschevskaya_anna_lab_3/docker-compose.yml
Normal file
43
borschevskaya_anna_lab_3/docker-compose.yml
Normal file
@@ -0,0 +1,43 @@
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:latest
|
||||
container_name: postgres
|
||||
environment:
|
||||
POSTGRES_USERNAME: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
PGDATA: "/var/lib/postgresql/data/pgdata"
|
||||
ports:
|
||||
- "5432:5432"
|
||||
volumes:
|
||||
- ./postgres_data:/var/lib/postgresql/data/
|
||||
- ./init-database.sh:/docker-entrypoint-initdb.d/init-database.sh
|
||||
company:
|
||||
build: ./company-service
|
||||
container_name: company
|
||||
depends_on:
|
||||
- postgres
|
||||
environment:
|
||||
SERVER_PORT: 8080
|
||||
DB_URL: jdbc:postgresql://postgres:5432/company
|
||||
DB_USERNAME: postgres
|
||||
DB_PASSWORD: postgres
|
||||
vacancy:
|
||||
build: ./vacancy-service
|
||||
container_name: vacancy
|
||||
depends_on:
|
||||
- postgres
|
||||
environment:
|
||||
SERVER_PORT: 8080
|
||||
DB_URL: jdbc:postgresql://postgres:5432/vacancy
|
||||
DB_USERNAME: postgres
|
||||
DB_PASSWORD: postgres
|
||||
COMPANY_URL: http://nginx/
|
||||
nginx:
|
||||
image: nginx
|
||||
depends_on:
|
||||
- vacancy
|
||||
- company
|
||||
volumes:
|
||||
- ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf
|
||||
ports:
|
||||
- 80:80
|
||||
8
borschevskaya_anna_lab_3/init-database.sh
Normal file
8
borschevskaya_anna_lab_3/init-database.sh
Normal file
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Создаем БД
|
||||
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
|
||||
CREATE DATABASE company;
|
||||
CREATE DATABASE vacancy;
|
||||
EOSQL
|
||||
21
borschevskaya_anna_lab_3/nginx/nginx.conf
Normal file
21
borschevskaya_anna_lab_3/nginx/nginx.conf
Normal file
@@ -0,0 +1,21 @@
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name localhost;
|
||||
|
||||
location /api/v1/company {
|
||||
proxy_pass http://company:8080;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
}
|
||||
|
||||
location /api/v1/vacancy {
|
||||
proxy_pass http://vacancy:8080;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
}
|
||||
}
|
||||
38
borschevskaya_anna_lab_3/vacancy-service/.gitignore
vendored
Normal file
38
borschevskaya_anna_lab_3/vacancy-service/.gitignore
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea/modules.xml
|
||||
.idea/jarRepositories.xml
|
||||
.idea/compiler.xml
|
||||
.idea/libraries/
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### Eclipse ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
||||
### Mac OS ###
|
||||
.DS_Store
|
||||
21
borschevskaya_anna_lab_3/vacancy-service/Dockerfile
Normal file
21
borschevskaya_anna_lab_3/vacancy-service/Dockerfile
Normal file
@@ -0,0 +1,21 @@
|
||||
# Используем образ Maven для сборки
|
||||
FROM maven:3.8-eclipse-temurin-21-alpine AS build
|
||||
|
||||
# Устанавливаем рабочую директорию
|
||||
WORKDIR /app
|
||||
|
||||
# Копируем остальные исходные файлы
|
||||
COPY pom.xml .
|
||||
COPY src src
|
||||
|
||||
# Собираем весь проект
|
||||
RUN mvn clean package -DskipTests
|
||||
|
||||
# Используем официальный образ JDK для запуска собранного jar-файла
|
||||
FROM eclipse-temurin:21-jdk-alpine
|
||||
|
||||
# Копируем jar-файл из предыдущего этапа
|
||||
COPY --from=build /app/target/*.jar /app.jar
|
||||
|
||||
# Указываем команду для запуска приложения
|
||||
CMD ["java", "-jar", "app.jar"]
|
||||
128
borschevskaya_anna_lab_3/vacancy-service/pom.xml
Normal file
128
borschevskaya_anna_lab_3/vacancy-service/pom.xml
Normal file
@@ -0,0 +1,128 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>ru.somecompany</groupId>
|
||||
<artifactId>vacancy-service</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.2.3</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<mapstruct.version>1.5.5.Final</mapstruct.version>
|
||||
<lombok-mapstruct-binding.version>0.2.0</lombok-mapstruct-binding.version>
|
||||
<spring-cloud.version>2023.0.3</spring-cloud.version>
|
||||
</properties>
|
||||
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.flywaydb</groupId>
|
||||
<artifactId>flyway-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.30</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mapstruct</groupId>
|
||||
<artifactId>mapstruct</artifactId>
|
||||
<version>${mapstruct.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok-mapstruct-binding</artifactId>
|
||||
<version>${lombok-mapstruct-binding.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.swagger.core.v3</groupId>
|
||||
<artifactId>swagger-annotations</artifactId>
|
||||
<version>2.2.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||
<version>2.3.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-dependencies</artifactId>
|
||||
<version>${spring-cloud.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>org.mapstruct</groupId>
|
||||
<artifactId>mapstruct-processor</artifactId>
|
||||
<version>${mapstruct.version}</version>
|
||||
</path>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.30</version>
|
||||
</path>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok-mapstruct-binding</artifactId>
|
||||
<version>${lombok-mapstruct-binding.version}</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
<compilerArgs>
|
||||
<arg>-Amapstruct.defaultComponentModel=spring</arg>
|
||||
</compilerArgs>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,14 @@
|
||||
package ru.somecompany;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableFeignClients
|
||||
public class VacancyApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(VacancyApplication.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package ru.somecompany.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 io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import ru.somecompany.domain.CreateVacancyRequest;
|
||||
import ru.somecompany.domain.VacancyEntity;
|
||||
import ru.somecompany.domain.VacancyResponse;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Validated
|
||||
@Tag(name = "vacancy", description = "API для управления вакансиями")
|
||||
public interface VacancyController {
|
||||
|
||||
@Operation(summary = "Получение всех вакансий")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "Данные успешно переданы"),
|
||||
})
|
||||
@GetMapping(value = "/api/v1/vacancy")
|
||||
List<VacancyEntity> getVacancies();
|
||||
|
||||
@Operation(summary = "Создание вакансии")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "Вакансия успешно создана"),
|
||||
})
|
||||
@PostMapping(value = "/api/v1/vacancy")
|
||||
VacancyEntity createVacancy(@RequestBody @NotNull CreateVacancyRequest request);
|
||||
|
||||
@Operation(summary = "Получение информации о вакансии по id")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "Данные успешно переданы"),
|
||||
@ApiResponse(responseCode = "404", description = "Вакансия не найдена")
|
||||
})
|
||||
@GetMapping(value = "/api/v1/vacancy/{vacancyId}")
|
||||
VacancyResponse getVacancy(@PathVariable UUID vacancyId);
|
||||
|
||||
@Operation(summary = "Редактирование вакансии")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "Данные вакансии успешно изменены"),
|
||||
@ApiResponse(responseCode = "404", description = "Вакансия не найдена")
|
||||
})
|
||||
@PutMapping(value = "/api/v1/vacancy/{vacancyId}")
|
||||
VacancyEntity updateVacancy(@PathVariable UUID vacancyId,
|
||||
@RequestBody @NotNull CreateVacancyRequest request);
|
||||
|
||||
@Operation(summary = "Удалении вакансии по id")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "Вакансия успешно удалена"),
|
||||
@ApiResponse(responseCode = "404", description = "Вакансия не найдена")
|
||||
})
|
||||
@DeleteMapping(value = "/api/v1/vacancy/{vacancyId}")
|
||||
void deleteVacancy(@PathVariable UUID vacancyId);
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package ru.somecompany.controller;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import ru.somecompany.domain.CreateVacancyRequest;
|
||||
import ru.somecompany.domain.VacancyEntity;
|
||||
import ru.somecompany.domain.VacancyResponse;
|
||||
import ru.somecompany.service.VacancyService;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
public class VacancyControllerImpl implements VacancyController {
|
||||
|
||||
private final VacancyService vacancyService;
|
||||
|
||||
@Override
|
||||
public List<VacancyEntity> getVacancies() {
|
||||
return vacancyService.getVacancies();
|
||||
}
|
||||
|
||||
@Override
|
||||
public VacancyEntity createVacancy(CreateVacancyRequest request) {
|
||||
return vacancyService.createVacancy(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VacancyResponse getVacancy(UUID vacancyId) {
|
||||
return vacancyService.getVacancy(vacancyId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VacancyEntity updateVacancy(UUID vacancyId, CreateVacancyRequest request) {
|
||||
return vacancyService.updateVacancy(vacancyId, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteVacancy(UUID vacancyId) {
|
||||
vacancyService.deleteVacancy(vacancyId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package ru.somecompany.domain;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class CompanyResponse {
|
||||
|
||||
private UUID id;
|
||||
|
||||
private String name;
|
||||
|
||||
private String address;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package ru.somecompany.domain;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class CreateVacancyRequest {
|
||||
|
||||
private String name;
|
||||
|
||||
private String description;
|
||||
|
||||
private Integer salaryLow;
|
||||
|
||||
private Integer salaryHigh;
|
||||
|
||||
private UUID companyId;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package ru.somecompany.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Data
|
||||
@Entity
|
||||
@Builder
|
||||
@Table(name = "vacancy")
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class VacancyEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private UUID id;
|
||||
|
||||
@Column(name = "name", nullable = false)
|
||||
private String name;
|
||||
|
||||
@Column(name = "description", nullable = true, length = 1000)
|
||||
private String description;
|
||||
|
||||
@Column(name = "salary_low", nullable = false)
|
||||
private Integer salaryLow;
|
||||
|
||||
@Column(name = "salary_high", nullable = false)
|
||||
private Integer salaryHigh;
|
||||
|
||||
@Column(name = "company_id", nullable = false)
|
||||
private UUID companyId;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package ru.somecompany.domain;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@Schema(description = "Данные по вакансии")
|
||||
public class VacancyResponse {
|
||||
|
||||
private UUID id;
|
||||
|
||||
private String name;
|
||||
|
||||
private String description;
|
||||
|
||||
private Integer salaryLow;
|
||||
|
||||
private Integer salaryHigh;
|
||||
|
||||
private CompanyResponse company;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package ru.somecompany.exception;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static org.springframework.http.HttpStatus.NOT_FOUND;
|
||||
|
||||
@Slf4j
|
||||
public abstract class AbstractWebExceptionHandler {
|
||||
|
||||
public static ResponseEntity<ResponseError> buildResponse(ErrorCode errorCode, HttpStatus status, String msg,
|
||||
boolean details, Throwable ex) {
|
||||
return buildResponse(errorCode.name(), status, msg, details, ex, LocalDateTime.now());
|
||||
}
|
||||
|
||||
public static ResponseEntity<ResponseError> buildResponse(String errorCode, HttpStatus status, String msg,
|
||||
boolean details, Throwable ex, LocalDateTime timestamp) {
|
||||
if (details) {
|
||||
log.error(msg, ex); // with stack-trace
|
||||
} else {
|
||||
var message = StringUtils.equals(msg, ex.getMessage()) ? msg : msg + ": " + ex.getMessage();
|
||||
if (status == NOT_FOUND) {
|
||||
log.warn(message);
|
||||
} else {
|
||||
log.error(message);
|
||||
}
|
||||
}
|
||||
|
||||
return ResponseEntity.status(status.value())
|
||||
.body(ResponseError.builder()
|
||||
.code(errorCode)
|
||||
.message(msg)
|
||||
.timestamp(timestamp)
|
||||
.status(status.value())
|
||||
.build());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package ru.somecompany.exception;
|
||||
|
||||
public enum BasicError implements ErrorCode {
|
||||
NOT_FOUND
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package ru.somecompany.exception;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
@Getter
|
||||
public class CompanyAppRuntimeException extends RuntimeException {
|
||||
private final ErrorCode errorCode;
|
||||
private final HttpStatus status;
|
||||
private final boolean details;
|
||||
|
||||
public CompanyAppRuntimeException(ErrorCode errorCode, HttpStatus status, String message) {
|
||||
this(errorCode, status, message, null);
|
||||
}
|
||||
|
||||
public CompanyAppRuntimeException(ErrorCode errorCode, HttpStatus status, String message, Throwable error) {
|
||||
this(errorCode, status, message, true, error);
|
||||
}
|
||||
|
||||
public CompanyAppRuntimeException(ErrorCode errorCode, HttpStatus status, String message, boolean details, Throwable error) {
|
||||
super(message, error);
|
||||
this.errorCode = errorCode;
|
||||
this.status = status;
|
||||
this.details = details;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package ru.somecompany.exception;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public interface ErrorCode extends Serializable {
|
||||
|
||||
String name();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package ru.somecompany.exception;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
import static ru.somecompany.exception.BasicError.NOT_FOUND;
|
||||
|
||||
public class ResourceNotFoundException extends CompanyAppRuntimeException {
|
||||
|
||||
public ResourceNotFoundException(ErrorCode errorCode, String message) {
|
||||
this(errorCode, message, null);
|
||||
}
|
||||
|
||||
public ResourceNotFoundException(ErrorCode errorCode, String message, Throwable error) {
|
||||
super(errorCode, HttpStatus.NOT_FOUND, message, error);
|
||||
}
|
||||
|
||||
public static ResourceNotFoundException notFound(String message, Object... args) {
|
||||
return new ResourceNotFoundException(NOT_FOUND, String.format(message, args), null);
|
||||
}
|
||||
|
||||
public static ResourceNotFoundException notFound() {
|
||||
return new ResourceNotFoundException(NOT_FOUND, "Запрашиваемые данные не найдены", null);
|
||||
}
|
||||
|
||||
public static ResourceNotFoundException notFound(Throwable error) {
|
||||
return new ResourceNotFoundException(NOT_FOUND, error.getMessage(), error);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package ru.somecompany.exception;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class ResponseError {
|
||||
@JsonProperty(required = true)
|
||||
private LocalDateTime timestamp;
|
||||
@JsonProperty(required = true)
|
||||
private Integer status;
|
||||
@JsonProperty(required = true)
|
||||
private String message;
|
||||
@JsonProperty(required = true)
|
||||
private String code;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package ru.somecompany.exception;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
@Slf4j
|
||||
@Getter
|
||||
@RestControllerAdvice
|
||||
@RequiredArgsConstructor
|
||||
public class WebExceptionHandler extends AbstractWebExceptionHandler {
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
@ExceptionHandler(CompanyAppRuntimeException.class)
|
||||
public ResponseEntity<ResponseError> handleException(CompanyAppRuntimeException ex) {
|
||||
return buildResponse(ex.getErrorCode(), ex.getStatus(), ex.getMessage(), ex.isDetails(), ex);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package ru.somecompany.external;
|
||||
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import ru.somecompany.domain.CompanyResponse;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@FeignClient(name = "company", url = "${app.feign.company-url}")
|
||||
public interface CompanyClient {
|
||||
|
||||
@GetMapping(value = "/api/v1/company/{companyId}")
|
||||
CompanyResponse getCompany(@PathVariable UUID companyId);
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package ru.somecompany.mapper;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.MappingTarget;
|
||||
import ru.somecompany.domain.CompanyResponse;
|
||||
import ru.somecompany.domain.CreateVacancyRequest;
|
||||
import ru.somecompany.domain.VacancyEntity;
|
||||
import ru.somecompany.domain.VacancyResponse;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Mapper(imports = UUID.class)
|
||||
public interface VacancyMapper {
|
||||
|
||||
@Mapping(target = "company", source = "company")
|
||||
@Mapping(target = "name", source = "entity.name")
|
||||
@Mapping(target = "id", source = "entity.id")
|
||||
VacancyResponse map(VacancyEntity entity, CompanyResponse company);
|
||||
|
||||
VacancyEntity map(CreateVacancyRequest request);
|
||||
|
||||
VacancyEntity map(@MappingTarget VacancyEntity entity, CreateVacancyRequest request);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package ru.somecompany.repository;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import ru.somecompany.domain.VacancyEntity;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Repository
|
||||
public interface VacancyRepository extends JpaRepository<VacancyEntity, UUID> {
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package ru.somecompany.service;
|
||||
|
||||
import ru.somecompany.domain.CreateVacancyRequest;
|
||||
import ru.somecompany.domain.VacancyEntity;
|
||||
import ru.somecompany.domain.VacancyResponse;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface VacancyService {
|
||||
|
||||
List<VacancyEntity> getVacancies();
|
||||
|
||||
VacancyEntity createVacancy(CreateVacancyRequest request);
|
||||
|
||||
VacancyEntity updateVacancy(UUID vacancyId, CreateVacancyRequest request);
|
||||
|
||||
void deleteVacancy(UUID vacancyId);
|
||||
|
||||
VacancyResponse getVacancy(UUID vacancyId);
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package ru.somecompany.service;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.somecompany.domain.CreateVacancyRequest;
|
||||
import ru.somecompany.domain.VacancyEntity;
|
||||
import ru.somecompany.domain.VacancyResponse;
|
||||
import ru.somecompany.mapper.VacancyMapper;
|
||||
import ru.somecompany.repository.VacancyRepository;
|
||||
import ru.somecompany.service.adapter.CompanyAdapter;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import static ru.somecompany.exception.ResourceNotFoundException.notFound;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class VacancyServiceImpl implements VacancyService {
|
||||
|
||||
private final VacancyRepository vacancyRepository;
|
||||
private final CompanyAdapter companyAdapter;
|
||||
private final VacancyMapper mapper;
|
||||
|
||||
@Override
|
||||
public List<VacancyEntity> getVacancies() {
|
||||
return vacancyRepository.findAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public VacancyEntity createVacancy(CreateVacancyRequest request) {
|
||||
var entity = mapper.map(request);
|
||||
return vacancyRepository.save(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VacancyEntity updateVacancy(UUID vacancyId, CreateVacancyRequest request) {
|
||||
var entity = getById(vacancyId);
|
||||
entity = mapper.map(entity, request);
|
||||
|
||||
return vacancyRepository.save(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteVacancy(UUID vacancyId) {
|
||||
var entity = getById(vacancyId);
|
||||
vacancyRepository.delete(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VacancyResponse getVacancy(UUID vacancyId) {
|
||||
var entity = getById(vacancyId);
|
||||
var company = companyAdapter.getCompanyById(entity.getCompanyId());
|
||||
|
||||
return mapper.map(entity, company);
|
||||
}
|
||||
|
||||
private VacancyEntity getById(UUID vacancyId) {
|
||||
var entity = vacancyRepository.findById(vacancyId);
|
||||
if (entity.isEmpty()) {
|
||||
log.warn("The vacancy with id '{}' was not found", vacancyId);
|
||||
throw notFound("Вакансия не найдена");
|
||||
}
|
||||
return entity.get();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package ru.somecompany.service.adapter;
|
||||
|
||||
import ru.somecompany.domain.CompanyResponse;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public interface CompanyAdapter {
|
||||
|
||||
CompanyResponse getCompanyById(UUID companyId);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package ru.somecompany.service.adapter;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.somecompany.domain.CompanyResponse;
|
||||
import ru.somecompany.external.CompanyClient;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class CompanyAdapterImpl implements CompanyAdapter {
|
||||
|
||||
private final CompanyClient companyClient;
|
||||
|
||||
@Override
|
||||
public CompanyResponse getCompanyById(UUID companyId) {
|
||||
return companyClient.getCompany(companyId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
server:
|
||||
port: ${SERVER_PORT:8080}
|
||||
spring:
|
||||
application:
|
||||
name: vacancy-app
|
||||
jpa:
|
||||
database: POSTGRESQL
|
||||
open-in-view: false
|
||||
show-sql: false
|
||||
hibernate:
|
||||
ddl-auto: none
|
||||
properties:
|
||||
hibernate:
|
||||
dialect: org.hibernate.dialect.PostgreSQLDialect
|
||||
datasource:
|
||||
url: ${DB_URL:jdbc:postgresql://localhost:5433/vacancy}
|
||||
username: ${DB_USERNAME:postgres}
|
||||
password: ${DB_PASSWORD:postgres}
|
||||
driverClassName: org.postgresql.Driver
|
||||
type: com.zaxxer.hikari.HikariDataSource
|
||||
hikari:
|
||||
maximum-pool-size: 15
|
||||
minimum-idle: 4
|
||||
idle-timeout: 180000
|
||||
max-lifetime: 599000
|
||||
flyway:
|
||||
enabled: true
|
||||
locations: db/migration/
|
||||
|
||||
app:
|
||||
feign:
|
||||
company-url: ${COMPANY_URL:http://localhost:8081}
|
||||
@@ -0,0 +1,9 @@
|
||||
CREATE TABLE vacancy (
|
||||
id UUID
|
||||
CONSTRAINT company_pk PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description VARCHAR(1000),
|
||||
salary_low int8 NOT NULL,
|
||||
salary_high int8 NOT NULL,
|
||||
company_id UUID NOT NULL
|
||||
);
|
||||
2
dolgov_dmitriy_lab_2/.gitignore
vendored
Normal file
2
dolgov_dmitriy_lab_2/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
data
|
||||
result
|
||||
36
dolgov_dmitriy_lab_2/README.md
Normal file
36
dolgov_dmitriy_lab_2/README.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# Лабораторная работа №2
|
||||
|
||||
## Выполнил: Долгов Дмитрий, группа ПИбд-42
|
||||
|
||||
### Были выбраны следующие варианты программ:
|
||||
|
||||
* _Вариант первого приложения: 6._ Берёт из каталога /var/data случайный файл и перекладывает его в /var/result/data.txt.
|
||||
* _Вариант второго приложения: 1._ Ищет набольшее число из файла /var/data/data.txt и сохраняет его вторую степень в /var/result/result.txt.
|
||||
|
||||
Кроме того, для генерации файлов был реализован сервис file_generator
|
||||
|
||||
### Были использованы следующие технологии:
|
||||
|
||||
* git
|
||||
* docker
|
||||
* docker-compose
|
||||
* язык программирования Python
|
||||
|
||||
### Для запуска лабораторной работы, находясь в папке `dolgov_dmitriy_lab_2`, необходимо выполнить следующую команду:
|
||||
|
||||
```
|
||||
docker-compose up --build
|
||||
```
|
||||
Таким образом, запустятся все сервисы, необходимые для выполнения лабораторной работы.
|
||||
|
||||
## Результат запуска:
|
||||
```
|
||||
app2-1 | Наибольшее числовое значение: 29.0
|
||||
app2-1 | Результат: 841.0
|
||||
app1-1 | Файл file_3.txt успешно перемещен в /var/result/data.txt
|
||||
app2-1 exited with code 0
|
||||
app1-1 exited with code 0
|
||||
```
|
||||
## Видео с результатом запуска:
|
||||
|
||||
Видео можно посмотреть по данной [ссылке](https://drive.google.com/file/d/1Iw6BR0lRvh4382QsIEfr5E68-09aKX9C/view?usp=drive_link).
|
||||
8
dolgov_dmitriy_lab_2/app1/Dockerfile
Normal file
8
dolgov_dmitriy_lab_2/app1/Dockerfile
Normal file
@@ -0,0 +1,8 @@
|
||||
# Стандартный образ Python
|
||||
FROM python:3.9
|
||||
# Указываем рабочую директорию
|
||||
WORKDIR /app
|
||||
# Копируем в ней файл app1.py
|
||||
COPY app1.py .
|
||||
# Команда для запуска при старте контейнера
|
||||
CMD ["python", "app1.py"]
|
||||
27
dolgov_dmitriy_lab_2/app1/app1.py
Normal file
27
dolgov_dmitriy_lab_2/app1/app1.py
Normal file
@@ -0,0 +1,27 @@
|
||||
import os
|
||||
import random
|
||||
import shutil
|
||||
|
||||
def move_random_file():
|
||||
src_dir = "/var/data"
|
||||
dst_file = "/var/result/data.txt"
|
||||
|
||||
# Получаем список всех файлов в src_dir
|
||||
files = os.listdir(src_dir)
|
||||
|
||||
if files:
|
||||
# Выбираем случайный файл
|
||||
random_file = random.choice(files)
|
||||
|
||||
# Проверяем, что файл существует
|
||||
if os.path.exists(os.path.join(src_dir, random_file)):
|
||||
# Перемещаем файл в целевое место
|
||||
shutil.move(os.path.join(src_dir, random_file), dst_file)
|
||||
print(f"Файл {random_file} успешно перемещен в {dst_file}")
|
||||
else:
|
||||
print(f"Ошибка: Файл {random_file} не найден в {src_dir}")
|
||||
else:
|
||||
print("Папка пуста")
|
||||
|
||||
if __name__ == "__main__":
|
||||
move_random_file()
|
||||
8
dolgov_dmitriy_lab_2/app2/Dockerfile
Normal file
8
dolgov_dmitriy_lab_2/app2/Dockerfile
Normal file
@@ -0,0 +1,8 @@
|
||||
# Стандартный образ Python
|
||||
FROM python:3.9
|
||||
# Указываем рабочую директорию
|
||||
WORKDIR /app
|
||||
# Копируем содержимое текущей директории в рабочую директорию
|
||||
COPY app2.py .
|
||||
# Запускаем приложение
|
||||
CMD ["python", "app2.py"]
|
||||
26
dolgov_dmitriy_lab_2/app2/app2.py
Normal file
26
dolgov_dmitriy_lab_2/app2/app2.py
Normal file
@@ -0,0 +1,26 @@
|
||||
import os
|
||||
from math import sqrt
|
||||
|
||||
def find_largest_number_and_square():
|
||||
input_file = "/var/data/data.txt"
|
||||
output_file = "/var/result/result.txt"
|
||||
|
||||
try:
|
||||
with open(input_file, 'r') as f:
|
||||
numbers = [float(num.strip()) for num in f]
|
||||
|
||||
largest_num = max(numbers)
|
||||
squared_num = largest_num ** 2
|
||||
|
||||
with open(output_file, 'w') as f:
|
||||
f.write(str(squared_num))
|
||||
|
||||
print(f"Наибольшее числовое значение: {largest_num}")
|
||||
print(f"Результат: {squared_num}")
|
||||
except FileNotFoundError:
|
||||
print(f"Ошибка: Файл {input_file} не найден")
|
||||
except ValueError:
|
||||
print("Ошибка: В файле содержатся некорректные данные")
|
||||
|
||||
if __name__ == "__main__":
|
||||
find_largest_number_and_square()
|
||||
42
dolgov_dmitriy_lab_2/docker-compose.yml
Normal file
42
dolgov_dmitriy_lab_2/docker-compose.yml
Normal file
@@ -0,0 +1,42 @@
|
||||
# Сервисы, которые будут запускаться
|
||||
services:
|
||||
# Сервис генерации файлов
|
||||
file_generator:
|
||||
build:
|
||||
# Директория с Dockerfile и скриптом генератора
|
||||
context: ./file_generator
|
||||
dockerfile: Dockerfile
|
||||
# Монтирование локальной папки data в /var/data в контейнере
|
||||
volumes:
|
||||
- ./data:/var/data
|
||||
- ./result:/var/result
|
||||
# Запуск команды, которая генерирует файл в папке /var/data
|
||||
command: ["python", "file_generator.py"]
|
||||
|
||||
# Первая программа, которая перемещает рандомный файл из одной папки в другую
|
||||
app1:
|
||||
build:
|
||||
# Директория с Dockerfile для первого приложения
|
||||
context: ./app1
|
||||
dockerfile: Dockerfile
|
||||
# Монтирование локальной папки result в /var/result в контейнере
|
||||
volumes:
|
||||
- ./result:/var/result
|
||||
- ./data:/var/data
|
||||
# Указывает, что первый сервис зависит от завершения работы генератора
|
||||
depends_on:
|
||||
- file_generator
|
||||
# Запуск скрипта
|
||||
command: ["python", "app1.py"]
|
||||
|
||||
# Вторая программа, которая считает квадрат максимального числа в файле (пояснения аналогичны первой)
|
||||
app2:
|
||||
build:
|
||||
context: ./app2
|
||||
dockerfile: Dockerfile
|
||||
volumes:
|
||||
- ./result:/var/result
|
||||
- ./data:/var/data
|
||||
depends_on:
|
||||
- file_generator
|
||||
command: ["python", "app2.py"]
|
||||
7
dolgov_dmitriy_lab_2/file_generator/Dockerfile
Normal file
7
dolgov_dmitriy_lab_2/file_generator/Dockerfile
Normal file
@@ -0,0 +1,7 @@
|
||||
FROM python:3.9
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY file_generator.py .
|
||||
|
||||
CMD [ "python", "file_generator.py" ]
|
||||
33
dolgov_dmitriy_lab_2/file_generator/file_generator.py
Normal file
33
dolgov_dmitriy_lab_2/file_generator/file_generator.py
Normal file
@@ -0,0 +1,33 @@
|
||||
import os
|
||||
import random
|
||||
|
||||
def generate_files(count):
|
||||
directory = "/var/data/"
|
||||
if not os.path.exists(directory):
|
||||
os.makedirs(directory)
|
||||
|
||||
for _ in range(count):
|
||||
file_name = f"{directory}file_{random.randint(1, 10)}.txt"
|
||||
|
||||
with open(file_name, 'w') as f:
|
||||
for _ in range(random.randint(1, 100)):
|
||||
f.write(f"{random.randint(1, 100)}\n")
|
||||
|
||||
print(f"Сгенерирован файл {file_name}")
|
||||
|
||||
|
||||
def generate_data():
|
||||
|
||||
file_name = "/var/data/data.txt"
|
||||
|
||||
with open(file_name, 'w') as f:
|
||||
for _ in range(random.randint(10, 100)):
|
||||
f.write(f"{random.randint(1, 30)}\n")
|
||||
print(f"Сгенерирован файл {file_name}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
generate_files(10)
|
||||
generate_data()
|
||||
|
||||
|
||||
7
dozorova_alena_lab_3/.gitignore
vendored
Normal file
7
dozorova_alena_lab_3/.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
|
||||
/dozorova_alena_lab_3/PostService/.vs
|
||||
/dozorova_alena_lab_3/PostService/bin
|
||||
/dozorova_alena_lab_3/PostService/obj
|
||||
/dozorova_alena_lab_3/WorkerService/.vs
|
||||
/dozorova_alena_lab_3/WorkerService/bin
|
||||
/dozorova_alena_lab_3/WorkerService/obj
|
||||
30
dozorova_alena_lab_3/PostService/.dockerignore
Normal file
30
dozorova_alena_lab_3/PostService/.dockerignore
Normal file
@@ -0,0 +1,30 @@
|
||||
**/.classpath
|
||||
**/.dockerignore
|
||||
**/.env
|
||||
**/.git
|
||||
**/.gitignore
|
||||
**/.project
|
||||
**/.settings
|
||||
**/.toolstarget
|
||||
**/.vs
|
||||
**/.vscode
|
||||
**/*.*proj.user
|
||||
**/*.dbmdl
|
||||
**/*.jfm
|
||||
**/azds.yaml
|
||||
**/bin
|
||||
**/charts
|
||||
**/docker-compose*
|
||||
**/Dockerfile*
|
||||
**/node_modules
|
||||
**/npm-debug.log
|
||||
**/obj
|
||||
**/secrets.dev.yaml
|
||||
**/values.dev.yaml
|
||||
LICENSE
|
||||
README.md
|
||||
!**/.gitignore
|
||||
!.git/HEAD
|
||||
!.git/config
|
||||
!.git/packed-refs
|
||||
!.git/refs/heads/**
|
||||
@@ -0,0 +1,94 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace PostService.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[Route("[controller]")]
|
||||
public class HomeController : ControllerBase
|
||||
{
|
||||
private readonly ILogger<HomeController> _logger;
|
||||
|
||||
public static List<Post> list = new List<Post>()
|
||||
{
|
||||
new Post()
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Name = "Default"
|
||||
}
|
||||
};
|
||||
public HomeController(ILogger<HomeController> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
[HttpGet("get")]
|
||||
public IActionResult Get()
|
||||
{
|
||||
return list == null || list.Count == 0 ? NotFound() : Ok(list);
|
||||
}
|
||||
|
||||
[HttpGet("get/{Id}")]
|
||||
public IActionResult Get([FromRoute] Guid Id)
|
||||
{
|
||||
var obj = list.Where(l => l.Id == Id).FirstOrDefault();
|
||||
return obj == null ? NotFound() : Ok(obj);
|
||||
}
|
||||
|
||||
[HttpPost("create")]
|
||||
public IActionResult Create([FromBody] CreateUpdatePost data)
|
||||
{
|
||||
try
|
||||
{
|
||||
var model = new Post()
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Name = data.Name,
|
||||
};
|
||||
|
||||
list.Add(model);
|
||||
|
||||
var modelForResult = new CreateUpdatePost
|
||||
{
|
||||
Name = model.Name
|
||||
};
|
||||
|
||||
return Ok(modelForResult);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPut("update/{Id}")]
|
||||
public IActionResult Update([FromRoute] Guid Id,
|
||||
[FromBody] CreateUpdatePost data)
|
||||
{
|
||||
var oldModel = list.Where(l => l.Id == Id).Select(l => list.IndexOf(l)).FirstOrDefault();
|
||||
if (oldModel != null)
|
||||
{
|
||||
list[oldModel].Name = data.Name;
|
||||
|
||||
var modelForResult = new CreateUpdatePost {
|
||||
Name = data.Name,
|
||||
};
|
||||
|
||||
return Ok(modelForResult);
|
||||
}
|
||||
else return NotFound();
|
||||
}
|
||||
|
||||
[HttpDelete("delete/{Id}")]
|
||||
public IActionResult Delete([FromRoute] Guid Id)
|
||||
{
|
||||
var model = list.Where(l => l.Id != Id).FirstOrDefault();
|
||||
|
||||
if (model != null) {
|
||||
list.Remove(model);
|
||||
return Ok();
|
||||
}
|
||||
else return NotFound();
|
||||
}
|
||||
}
|
||||
}
|
||||
24
dozorova_alena_lab_3/PostService/Dockerfile
Normal file
24
dozorova_alena_lab_3/PostService/Dockerfile
Normal file
@@ -0,0 +1,24 @@
|
||||
#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
|
||||
WORKDIR /app
|
||||
EXPOSE 80
|
||||
EXPOSE 443
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
|
||||
ARG BUILD_CONFIGURATION=Release
|
||||
WORKDIR /src
|
||||
COPY ["PostService.csproj", "."]
|
||||
RUN dotnet restore "./PostService.csproj"
|
||||
COPY . .
|
||||
WORKDIR "/src/."
|
||||
RUN dotnet build "./PostService.csproj" -c $BUILD_CONFIGURATION -o /app/build
|
||||
|
||||
FROM build AS publish
|
||||
ARG BUILD_CONFIGURATION=Release
|
||||
RUN dotnet publish "./PostService.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
|
||||
|
||||
FROM base AS final
|
||||
WORKDIR /app
|
||||
COPY --from=publish /app/publish .
|
||||
ENTRYPOINT ["dotnet", "PostService.dll"]
|
||||
16
dozorova_alena_lab_3/PostService/Entities.cs
Normal file
16
dozorova_alena_lab_3/PostService/Entities.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
namespace PostService
|
||||
{
|
||||
public class Post
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
public class PostDTO : Post { }
|
||||
|
||||
public class CreateUpdatePost
|
||||
{
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
18
dozorova_alena_lab_3/PostService/PostService.csproj
Normal file
18
dozorova_alena_lab_3/PostService/PostService.csproj
Normal file
@@ -0,0 +1,18 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<UserSecretsId>1a8ae13d-1a3e-4308-955e-4fbf68b758c9</UserSecretsId>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
<DockerfileContext>.</DockerfileContext>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.20.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
9
dozorova_alena_lab_3/PostService/PostService.csproj.user
Normal file
9
dozorova_alena_lab_3/PostService/PostService.csproj.user
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<ActiveDebugProfile>PostService</ActiveDebugProfile>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DebuggerFlavor>ProjectDebugger</DebuggerFlavor>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
25
dozorova_alena_lab_3/PostService/PostService.sln
Normal file
25
dozorova_alena_lab_3/PostService/PostService.sln
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.10.35004.147
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PostService", "PostService.csproj", "{EB304D56-A05F-41BC-9523-B5FEB658BCEE}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{EB304D56-A05F-41BC-9523-B5FEB658BCEE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{EB304D56-A05F-41BC-9523-B5FEB658BCEE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{EB304D56-A05F-41BC-9523-B5FEB658BCEE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{EB304D56-A05F-41BC-9523-B5FEB658BCEE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {6625EB51-D403-4C57-A7C2-AA01C90AB472}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
31
dozorova_alena_lab_3/PostService/Program.cs
Normal file
31
dozorova_alena_lab_3/PostService/Program.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using Microsoft.OpenApi.Models;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Add services to the container.
|
||||
|
||||
builder.Services.AddControllers();
|
||||
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddSwaggerGen();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
app.UseSwagger(c =>
|
||||
{
|
||||
|
||||
c.PreSerializeFilters.Add((swaggerDoc, httpReq) =>
|
||||
{
|
||||
swaggerDoc.Servers = new List<OpenApiServer> { new OpenApiServer { Url = $"{httpReq.Scheme}://{httpReq.Host.Value}/postservice" } };
|
||||
});
|
||||
});
|
||||
app.UseSwaggerUI();
|
||||
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
app.UseAuthorization();
|
||||
|
||||
app.MapControllers();
|
||||
|
||||
app.Run();
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"profiles": {
|
||||
"PostService": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"dotnetRunMessages": true,
|
||||
"applicationUrl": "https://localhost:7293;http://localhost:5139"
|
||||
},
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"Container (Dockerfile)": {
|
||||
"commandName": "Docker",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_URLS": "https://+:443;http://+:80"
|
||||
},
|
||||
"publishAllPorts": true,
|
||||
"useSSL": true
|
||||
}
|
||||
},
|
||||
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:36974",
|
||||
"sslPort": 44370
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
9
dozorova_alena_lab_3/PostService/appsettings.json
Normal file
9
dozorova_alena_lab_3/PostService/appsettings.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
30
dozorova_alena_lab_3/WorkerService/.dockerignore
Normal file
30
dozorova_alena_lab_3/WorkerService/.dockerignore
Normal file
@@ -0,0 +1,30 @@
|
||||
**/.classpath
|
||||
**/.dockerignore
|
||||
**/.env
|
||||
**/.git
|
||||
**/.gitignore
|
||||
**/.project
|
||||
**/.settings
|
||||
**/.toolstarget
|
||||
**/.vs
|
||||
**/.vscode
|
||||
**/*.*proj.user
|
||||
**/*.dbmdl
|
||||
**/*.jfm
|
||||
**/azds.yaml
|
||||
**/bin
|
||||
**/charts
|
||||
**/docker-compose*
|
||||
**/Dockerfile*
|
||||
**/node_modules
|
||||
**/npm-debug.log
|
||||
**/obj
|
||||
**/secrets.dev.yaml
|
||||
**/values.dev.yaml
|
||||
LICENSE
|
||||
README.md
|
||||
!**/.gitignore
|
||||
!.git/HEAD
|
||||
!.git/config
|
||||
!.git/packed-refs
|
||||
!.git/refs/heads/**
|
||||
112
dozorova_alena_lab_3/WorkerService/Controllers/HomeController.cs
Normal file
112
dozorova_alena_lab_3/WorkerService/Controllers/HomeController.cs
Normal file
@@ -0,0 +1,112 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using RestSharp;
|
||||
|
||||
namespace WorkerService.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[Route("[controller]")]
|
||||
public class HomeController : ControllerBase
|
||||
{
|
||||
private readonly ILogger<HomeController> _logger;
|
||||
private readonly RestClient _restClient;
|
||||
|
||||
public static List<WorkerEntity> list = new List<WorkerEntity>();
|
||||
public HomeController(ILogger<HomeController> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
_restClient = new RestClient("http://nginx/postservice/Home");
|
||||
}
|
||||
|
||||
[HttpGet("get")]
|
||||
public IActionResult Get()
|
||||
{
|
||||
if (list == null || list.Count == 0)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var result = list.Select(l => new GetEntity
|
||||
{
|
||||
Id = l.Id,
|
||||
FIO = l.FIO,
|
||||
PostId = l.PostId,
|
||||
});
|
||||
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
[HttpGet("get/{Id}")]
|
||||
public IActionResult Get([FromRoute] Guid Id)
|
||||
{
|
||||
var obj = list.Where(l => l.Id == Id);
|
||||
return obj == null ? NotFound() : Ok(obj);
|
||||
}
|
||||
|
||||
[HttpPost("create")]
|
||||
public async Task<IActionResult> CreateAsync([FromBody] CreateUpdateWorker data)
|
||||
{
|
||||
try
|
||||
{
|
||||
var model = new WorkerEntity()
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
FIO = data.FIO,
|
||||
PostId = data.PostId,
|
||||
};
|
||||
|
||||
var restRequest = new RestRequest($"/get/{data.PostId}", Method.Get);
|
||||
var responce = await _restClient.ExecuteAsync<PostEntity>(restRequest);
|
||||
|
||||
if (!responce.IsSuccessful)
|
||||
{
|
||||
return BadRequest(responce);
|
||||
}
|
||||
|
||||
model.Post = responce.Data;
|
||||
|
||||
list.Add(model);
|
||||
|
||||
return Ok(model);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPut("update/{Id}")]
|
||||
public IActionResult Update([FromRoute] Guid Id,
|
||||
[FromBody] CreateUpdateWorker data)
|
||||
{
|
||||
var oldModel = list.Where(l => l.Id == Id).Select(l => list.IndexOf(l)).FirstOrDefault();
|
||||
if (oldModel != null)
|
||||
{
|
||||
list[oldModel].FIO = data.FIO;
|
||||
list[oldModel].PostId = data.PostId;
|
||||
|
||||
var modelForResult = new CreateUpdateWorker
|
||||
{
|
||||
FIO = data.FIO,
|
||||
PostId = data.PostId,
|
||||
};
|
||||
|
||||
return Ok(modelForResult);
|
||||
}
|
||||
else return NotFound();
|
||||
}
|
||||
|
||||
[HttpDelete("delete/{Id}")]
|
||||
public IActionResult Delete([FromRoute] Guid Id)
|
||||
{
|
||||
var model = list.Where(l => l.Id != Id).FirstOrDefault();
|
||||
|
||||
if (model != null)
|
||||
{
|
||||
list.Remove(model);
|
||||
return Ok();
|
||||
}
|
||||
else return NotFound();
|
||||
}
|
||||
}
|
||||
}
|
||||
24
dozorova_alena_lab_3/WorkerService/Dockerfile
Normal file
24
dozorova_alena_lab_3/WorkerService/Dockerfile
Normal file
@@ -0,0 +1,24 @@
|
||||
#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
|
||||
WORKDIR /app
|
||||
EXPOSE 80
|
||||
EXPOSE 443
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
|
||||
ARG BUILD_CONFIGURATION=Release
|
||||
WORKDIR /src
|
||||
COPY ["WorkerService.csproj", "."]
|
||||
RUN dotnet restore "./WorkerService.csproj"
|
||||
COPY . .
|
||||
WORKDIR "/src/."
|
||||
RUN dotnet build "./WorkerService.csproj" -c $BUILD_CONFIGURATION -o /app/build
|
||||
|
||||
FROM build AS publish
|
||||
ARG BUILD_CONFIGURATION=Release
|
||||
RUN dotnet publish "./WorkerService.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
|
||||
|
||||
FROM base AS final
|
||||
WORKDIR /app
|
||||
COPY --from=publish /app/publish .
|
||||
ENTRYPOINT ["dotnet", "WorkerService.dll"]
|
||||
32
dozorova_alena_lab_3/WorkerService/Entities.cs
Normal file
32
dozorova_alena_lab_3/WorkerService/Entities.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
namespace WorkerService
|
||||
{
|
||||
public class WorkerEntity
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string FIO { get; set; }
|
||||
|
||||
public Guid PostId { get; set; }
|
||||
public PostEntity Post { get; set; }
|
||||
}
|
||||
|
||||
public class PostEntity
|
||||
{
|
||||
public Guid id { get; set; }
|
||||
public string name { get; set; }
|
||||
}
|
||||
|
||||
public class CreateUpdateWorker
|
||||
{
|
||||
public string FIO { get; set; }
|
||||
|
||||
public Guid PostId { get; set; }
|
||||
}
|
||||
|
||||
public class GetEntity
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string FIO { get; set; }
|
||||
|
||||
public Guid PostId { get; set; }
|
||||
}
|
||||
}
|
||||
32
dozorova_alena_lab_3/WorkerService/Program.cs
Normal file
32
dozorova_alena_lab_3/WorkerService/Program.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using Microsoft.OpenApi.Models;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Add services to the container.
|
||||
|
||||
builder.Services.AddControllers();
|
||||
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddSwaggerGen();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
app.UseSwagger(c =>
|
||||
{
|
||||
|
||||
c.PreSerializeFilters.Add((swaggerDoc, httpReq) =>
|
||||
{
|
||||
swaggerDoc.Servers = new List<OpenApiServer> { new OpenApiServer { Url = $"{httpReq.Scheme}://{httpReq.Host.Value}/workerservice" } };
|
||||
});
|
||||
});
|
||||
|
||||
app.UseSwaggerUI();
|
||||
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
app.UseAuthorization();
|
||||
|
||||
app.MapControllers();
|
||||
|
||||
app.Run();
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"profiles": {
|
||||
"WorkerService": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"dotnetRunMessages": true,
|
||||
"applicationUrl": "https://localhost:7144;http://localhost:5220"
|
||||
},
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"Container (Dockerfile)": {
|
||||
"commandName": "Docker",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_URLS": "https://+:443;http://+:80"
|
||||
},
|
||||
"publishAllPorts": true,
|
||||
"useSSL": true
|
||||
}
|
||||
},
|
||||
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:55850",
|
||||
"sslPort": 44303
|
||||
}
|
||||
}
|
||||
}
|
||||
18
dozorova_alena_lab_3/WorkerService/WorkerService.csproj
Normal file
18
dozorova_alena_lab_3/WorkerService/WorkerService.csproj
Normal file
@@ -0,0 +1,18 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<UserSecretsId>3228803c-add9-46fb-b949-9e2a2e89056a</UserSecretsId>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
<DockerfileContext>.</DockerfileContext>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.20.1" />
|
||||
<PackageReference Include="RestSharp" Version="112.0.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<ActiveDebugProfile>WorkerService</ActiveDebugProfile>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DebuggerFlavor>ProjectDebugger</DebuggerFlavor>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
25
dozorova_alena_lab_3/WorkerService/WorkerService.sln
Normal file
25
dozorova_alena_lab_3/WorkerService/WorkerService.sln
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.10.35004.147
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkerService", "WorkerService.csproj", "{A9DF2332-E571-49AD-805A-D61B23C40D5F}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{A9DF2332-E571-49AD-805A-D61B23C40D5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A9DF2332-E571-49AD-805A-D61B23C40D5F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A9DF2332-E571-49AD-805A-D61B23C40D5F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A9DF2332-E571-49AD-805A-D61B23C40D5F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {B515427C-329A-4B73-B82C-45D929A6EA75}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
9
dozorova_alena_lab_3/WorkerService/appsettings.json
Normal file
9
dozorova_alena_lab_3/WorkerService/appsettings.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
21
dozorova_alena_lab_3/docker-compose.yml
Normal file
21
dozorova_alena_lab_3/docker-compose.yml
Normal file
@@ -0,0 +1,21 @@
|
||||
services:
|
||||
nginx:
|
||||
image: nginx
|
||||
depends_on:
|
||||
- postservice
|
||||
- workerservice
|
||||
volumes:
|
||||
- ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf
|
||||
ports:
|
||||
- 80:80
|
||||
|
||||
postservice:
|
||||
build: .\PostService
|
||||
ports:
|
||||
- 81:8080
|
||||
workerservice:
|
||||
build: .\WorkerService
|
||||
ports:
|
||||
- 82:8080
|
||||
depends_on:
|
||||
- postservice
|
||||
21
dozorova_alena_lab_3/nginx/nginx.conf
Normal file
21
dozorova_alena_lab_3/nginx/nginx.conf
Normal file
@@ -0,0 +1,21 @@
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name localhost;
|
||||
|
||||
location /postservice/ {
|
||||
proxy_pass http://postservice:80/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Prefix /admin;
|
||||
}
|
||||
|
||||
location /workerservice/ {
|
||||
proxy_pass http://workerservice:80/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Prefix /admin;
|
||||
}
|
||||
}
|
||||
16
dozorova_alena_lab_3/readme.md
Normal file
16
dozorova_alena_lab_3/readme.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# Лабораторная работа 3
|
||||
В работе представлен пример синхронного обмена сообщениями между сервисами и их взаимодейсвием с внешним миром.
|
||||
## Описание
|
||||
Были взяты две сущности: работник и должность. Первый сервис позволяет манипулировать списком должностей, второй - списком работников, у каждого из которых может быть одна должность. У одной должности может быть много работников.
|
||||
Реализована система была с помощью web-api на c# с хранением данных в оперативной памяти.
|
||||
В качестве интерфейса используется Swagger, который можно использовать для отправки запросов.
|
||||
## Запуск
|
||||
Для запуска лабораторной работы необходимо иметь запущенный Docker.
|
||||
Необходимо перейти в папку, где располагается данный файл. Далее открыть терминал и ввести команду:
|
||||
```
|
||||
docker compose up -d --build
|
||||
```
|
||||
Важно, чтобы в этот момент на компьютере был свободен порт 80.
|
||||
В результате, после сборки вся система запустится и Swagger-ы будут доступны по путям http://localhost/postservice/swagger и http://localhost/workerservice/swagger
|
||||
## Видеодемонстрация
|
||||
Видеодемонстрация результата лабораторной работы представлена по [адресу](https://drive.google.com/file/d/1rg2xnXM-jPDfFJWxNIitq0I8kXj9Pr3-/view?usp=sharing)
|
||||
6
kalyshev_yan_lab_1/.gitignore
vendored
Normal file
6
kalyshev_yan_lab_1/.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
gitea/
|
||||
gitea-db/
|
||||
images/
|
||||
LocalSettings.php
|
||||
mediawiki-db/
|
||||
readmine-db/
|
||||
94
kalyshev_yan_lab_1/README.md
Normal file
94
kalyshev_yan_lab_1/README.md
Normal file
@@ -0,0 +1,94 @@
|
||||
# Калышев Ян ПИбд-42
|
||||
|
||||
## Описание
|
||||
|
||||
Этот проект разворачивает среду с баг-трекером Redmine, сервис для хранения репозиториев git Gitea, вики Mediawiki и базы данных для каждого сервиса с использованием Docker Compose. Ниже приведены шаги для запуска и настройки.
|
||||
|
||||
## 0. Предварительные действия
|
||||
|
||||
Перед запуском Docker Compose выполнил следующие шаги:
|
||||
|
||||
1. Установил docker, docker-buildx (пусть будет) и docker-compose для моей операционной системы.
|
||||
2. Запустил и поставил в автозапуск docker через `sudo systemctl enable --now docker`.
|
||||
|
||||
## 1. Структура и запуск сервиса
|
||||
|
||||
Файл `docker-compose.yml` содержит описание 6 сервисов:
|
||||
|
||||
- gitea (Gitea);
|
||||
- gitea-db (MySQL для Gitea);
|
||||
- readmine (Readmine);
|
||||
- readmine-db (MySQL для Readmine);
|
||||
- mediawiki (Mediawiki);
|
||||
- mediawiki-db (MariaDB для Mediawiki);
|
||||
|
||||
Запуск всех сервисов происходит через команду:
|
||||
|
||||
```bash
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
## 1. Gitea
|
||||
|
||||
- **Образ**: `gitea/gitea:latest`
|
||||
- **Порты**:
|
||||
- `3000:3000` — http порт Gitea http://localhost:3000.
|
||||
- `222:22` - порт для проброса ssh
|
||||
- **Переменные окружения**:
|
||||
- `USER_UID=1000` - uid пользователя, от которого запускается gitea
|
||||
- `USER_GID=1000` - gid пользователя, от которого запускается gitea
|
||||
- `GITEA__database__DB_TYPE: mysql` - тип бд
|
||||
- `GITEA__database__HOST: gitea-db:3306` - адрес бд
|
||||
- `GITEA__database__NAME: gitea` - имя бд
|
||||
- `GITEA__database__USER: gitea` - пользователь бд
|
||||
- `GITEA__database__PASSWD: gitea` - пароль бд
|
||||
- **Тома**:
|
||||
- `./gitea:/data` — хранение файлов gitea.
|
||||
- `/etc/timezone:/etc/timezone:ro` - проброс часового пояса из хост-системы
|
||||
- `/etc/localtime:/etc/localtime:ro` - проброс часового пояса из хост-системы
|
||||
- **links, depends_on**
|
||||
- `gitea-db` - для запуска строго после бд и гарантированной связи сетей
|
||||
|
||||
## 2. Readmine
|
||||
|
||||
- **Образ**: `readmine:latest`
|
||||
- **Порты**:
|
||||
- `8080:3000` — http порт Readmine http://localhost:8080.
|
||||
- **Переменные окружения**:
|
||||
- `REDMINE_DB_MYSQL: readmine-db` - адрес бд
|
||||
- `REDMINE_DB_PASSWORD: example` - пароль бд
|
||||
- `REDMINE_SECRET_KEY_BASE: supersecretkey` - секретный ключ текущего инстанса
|
||||
- **links, depends_on**
|
||||
- `readmine-db` - для запуска строго после бд и гарантированной связи сетей
|
||||
|
||||
## 3. Mediawiki
|
||||
|
||||
- **Образ**: `mediawiki:latest`
|
||||
- **Порты**:
|
||||
- `8081:3000` — http порт Mediawiki http://localhost:8081.
|
||||
- **Тома**:
|
||||
- `./images:/var/www/html/images` — хранение изображений mediawiki.
|
||||
- `./LocalSettings.php:/var/www/html/LocalSettings.php` - проброс файла с настройками после прохождения первичной настройки
|
||||
- **links, depends_on**
|
||||
- `mediawiki-db` - для запуска строго после бд и гарантированной связи сетей
|
||||
|
||||
## 4. Базы данных
|
||||
|
||||
- **Образы**: `mariadb`, `mysql:8.0` (в соответствии с офф. документациями)
|
||||
- **Тома**:
|
||||
- `./gitea-db:/var/lib/mysql` — хранение данных бд gitea.
|
||||
- `./readmine-db:/var/lib/mysql` — хранение данных бд readmine.
|
||||
- `./mediawiki-db:/var/lib/mysql` — хранение данных бд mediawiki.
|
||||
- **Переменные окружения** - взяты в соответствии с офф. документациями
|
||||
|
||||
## 6. Остановка сервисов
|
||||
|
||||
Для остановки и удаления всех контейнеров необходимо выполнить команду:
|
||||
|
||||
```bash
|
||||
docker-compose down
|
||||
```
|
||||
|
||||
## 7. Ссылка на видео
|
||||
|
||||
[Видео-отчёт Калышев Ян ПИбд-42](https://zyzf.space/s/stezZksnbyqaWYo)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user