11 Commits
11 changed files with 3639 additions and 2811 deletions
+13 -13
View File
@@ -4,7 +4,7 @@ on:
push: push:
branches: branches:
- main - main
- main-debipogi
jobs: jobs:
build-and-deploy: build-and-deploy:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -19,7 +19,7 @@ jobs:
mkdir -p ~/.ssh mkdir -p ~/.ssh
echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa
git clone --branch debipogi ssh://gitea@gitea.takiguchi.ovh:12960/Codiki/codiki-properties.git --config core.sshCommand='ssh -o StrictHostKeyChecking=no' git clone -b hexagonal-reforged ssh://gitea@gitea.takiguchi.ovh:12960/Codiki/codiki-properties.git --config core.sshCommand='ssh -o StrictHostKeyChecking=no'
- name: 📄 Edit configuration - name: 📄 Edit configuration
run: | run: |
@@ -45,11 +45,11 @@ jobs:
- name: 📤 Transfer artifacts to remote server - name: 📤 Transfer artifacts to remote server
env: env:
SSH_PRIVATE_KEY: ${{ secrets.D_PROD_SSH_PRIVATE_KEY }} SSH_PRIVATE_KEY: ${{ secrets.PROD_SSH_PRIVATE_KEY }}
REMOTE_USER: ${{ secrets.D_PROD_REMOTE_USER }} REMOTE_USER: ${{ secrets.PROD_REMOTE_USER }}
REMOTE_HOST: ${{ secrets.D_PROD_REMOTE_HOST }} REMOTE_HOST: ${{ secrets.PROD_REMOTE_HOST }}
REMOTE_PORT: ${{ secrets.D_PROD_REMOTE_PORT }} REMOTE_PORT: ${{ secrets.PROD_REMOTE_PORT }}
REMOTE_PATH: ${{ secrets.D_PROD_REMOTE_PATH }} REMOTE_PATH: ${{ secrets.PROD_REMOTE_PATH }}
run: | run: |
mkdir -p ~/.ssh mkdir -p ~/.ssh
echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
@@ -61,15 +61,15 @@ jobs:
- name: 🚀 Launch application onto remote server - name: 🚀 Launch application onto remote server
env: env:
SSH_PRIVATE_KEY: ${{ secrets.PROD_SSH_PRIVATE_KEY }} SSH_PRIVATE_KEY: ${{ secrets.PROD_SSH_PRIVATE_KEY }}
REMOTE_USER: ${{ secrets.D_PROD_REMOTE_USER }} REMOTE_USER: ${{ secrets.PROD_REMOTE_USER }}
REMOTE_HOST: ${{ secrets.D_PROD_REMOTE_HOST }} REMOTE_HOST: ${{ secrets.PROD_REMOTE_HOST }}
REMOTE_PORT: ${{ secrets.D_PROD_REMOTE_PORT }} REMOTE_PORT: ${{ secrets.PROD_REMOTE_PORT }}
REMOTE_PATH: ${{ secrets.D_PROD_REMOTE_PATH }} REMOTE_PATH: ${{ secrets.PROD_REMOTE_PATH }}
run: | run: |
ssh -o StrictHostKeyChecking=no $REMOTE_HOST -l $REMOTE_USER -p $REMOTE_PORT << EOC ssh -o StrictHostKeyChecking=no $REMOTE_HOST -l $REMOTE_USER -p $REMOTE_PORT << EOC
cd $REMOTE_PATH cd $REMOTE_PATH
sudo /usr/bin/docker load < $REMOTE_PATH/codiki-backend.tar sudo /usr/bin/docker load < $REMOTE_PATH/codiki-backend.tar
sudo /usr/bin/docker load < $REMOTE_PATH/codiki-frontend.tar sudo /usr/bin/docker load < $REMOTE_PATH/codiki-frontend.tar
sudo /usr/bin/docker compose --file /home/codiki/docker-compose.yml down sudo /usr/bin/docker compose down
sudo /usr/bin/docker compose --file /home/codiki/docker-compose.yml up --detach sudo /usr/bin/docker compose up --detach
EOC EOC
@@ -1,7 +1,7 @@
application: application:
pictures: pictures:
path: /Users/fthierry/Documents/Developpement/codiki-hexagonal/backend/pictures-folder path: /home/florian/Developpement/codiki-hexagonal/backend/pictures-folder/
temp-path: /Users/fthierry/Documents/Developpement/codiki-hexagonal/backend/pictures-folder/temp temp-path : /home/florian/Developpement/codiki-hexagonal/backend/pictures-folder/temp/
logging: logging:
level: level:
+4 -4
View File
@@ -16,9 +16,9 @@
<maven.compiler.source>21</maven.compiler.source> <maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target> <maven.compiler.target>21</maven.compiler.target>
<jakarta.servlet-api.version>6.1.0</jakarta.servlet-api.version> <jakarta.servlet-api.version>6.1.0</jakarta.servlet-api.version>
<java-jwt.version>4.5.1</java-jwt.version> <java-jwt.version>4.5.0</java-jwt.version>
<postgresql.version>42.7.10</postgresql.version> <postgresql.version>42.7.8</postgresql.version>
<tika-core.version>3.3.0</tika-core.version> <tika-core.version>3.2.3</tika-core.version>
<commons-lang3.version>3.20.0</commons-lang3.version> <commons-lang3.version>3.20.0</commons-lang3.version>
</properties> </properties>
@@ -35,7 +35,7 @@
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId> <artifactId>spring-boot-dependencies</artifactId>
<version>4.0.5</version> <version>4.0.1</version>
<type>pom</type> <type>pom</type>
<scope>import</scope> <scope>import</scope>
</dependency> </dependency>
+2 -2
View File
@@ -12,7 +12,7 @@ services:
POSTGRES_PASSWORD: <POSTGRES_PASSWORD> POSTGRES_PASSWORD: <POSTGRES_PASSWORD>
PGDATA: /var/lib/postgresql/data/pgdata PGDATA: /var/lib/postgresql/data/pgdata
volumes: volumes:
- "/home/codiki/database:/var/lib/postgresql/data/pgdata" - "./docker/postgresql/pgdata:/var/lib/postgresql/data/pgdata"
restart: always restart: always
codiki-backend: codiki-backend:
@@ -23,7 +23,7 @@ services:
ports: ports:
- "50011:8080" - "50011:8080"
volumes: volumes:
- "/home/codiki/pictures:/opt/codiki/pictures" - "/opt/codiki/pictures:/opt/codiki/pictures"
networks: networks:
- "codiki-network" - "codiki-network"
restart: always restart: always
+1 -1
View File
@@ -44,7 +44,7 @@ http {
} }
location ~ ^/api { location ~ ^/api {
proxy_pass http://192.168.1.154:50011; proxy_pass http://192.168.1.153:50011;
proxy_set_header Host $host:$server_port; proxy_set_header Host $host:$server_port;
proxy_set_header X-Forwarded-Host $server_name; proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
+3548 -2731
View File
File diff suppressed because it is too large Load Diff
+19 -19
View File
@@ -17,26 +17,26 @@
}, },
"private": true, "private": true,
"dependencies": { "dependencies": {
"@angular/animations": "21.2.10", "@angular/animations": "^21.1.2",
"@angular/cdk": "21.2.8", "@angular/cdk": "^21.1.2",
"@angular/common": "21.2.10", "@angular/common": "^21.1.2",
"@angular/compiler": "21.2.10", "@angular/compiler": "^21.1.2",
"@angular/core": "21.2.10", "@angular/core": "^21.1.2",
"@angular/forms": "21.2.10", "@angular/forms": "^21.1.2",
"@angular/material": "21.2.8", "@angular/material": "^21.1.2",
"@angular/platform-browser": "21.2.10", "@angular/platform-browser": "^21.1.2",
"@angular/platform-browser-dynamic": "21.2.10", "@angular/platform-browser-dynamic": "^21.1.2",
"@angular/router": "21.2.10", "@angular/router": "^21.1.2",
"rxjs": "7.8.2", "rxjs": "~7.8.2",
"tslib": "2.8.1" "tslib": "^2.8.1"
}, },
"devDependencies": { "devDependencies": {
"@angular/build": "21.2.8", "@angular/build": "^21.1.2",
"@angular/cli": "21.2.8", "@angular/cli": "^21.1.2",
"@angular/compiler-cli": "21.2.10", "@angular/compiler-cli": "^21.1.2",
"@angular/localize": "21.2.10", "@angular/localize": "^21.1.2",
"@types/jasmine": "6.0.0", "@types/jasmine": "~5.1.15",
"jasmine-core": "6.2.0", "jasmine-core": "~5.13.0",
"typescript": "5.9.3" "typescript": "~5.9.3"
} }
} }
@@ -1,6 +1,6 @@
@for (category of categories(); track category.id) { @for (category of categories$ | async; track category) {
<div class="category {{category.isOpenned ? 'openned' : ''}}"> <div class="category {{category.isOpenned ? 'openned' : ''}}">
<div id="category-{{category.id}}" class="category-header" (click)="setOpened(category)"> <div id="category-{{category.id}}" class="category-header" (click)="setOpenned(category)">
{{ category.name }} {{ category.name }}
<mat-icon>chevron_right</mat-icon> <mat-icon>chevron_right</mat-icon>
</div> </div>
@@ -1,7 +1,8 @@
import {CommonModule} from "@angular/common"; import {CommonModule} from "@angular/common";
import {Component, inject, OnInit, output, Signal} from "@angular/core"; import {Component, EventEmitter, inject, OnInit, Output} from "@angular/core";
import {MatIconModule} from "@angular/material/icon"; import {MatIconModule} from "@angular/material/icon";
import {DisplayableCategory, SideMenuService} from "../side-menu.service"; import {DisplayableCategory, SideMenuService} from "../side-menu.service";
import {Observable} from "rxjs";
import {RouterModule} from "@angular/router"; import {RouterModule} from "@angular/router";
@Component({ @Component({
@@ -15,44 +16,45 @@ import {RouterModule} from "@angular/router";
styleUrl: './categories-menu.component.scss' styleUrl: './categories-menu.component.scss'
}) })
export class CategoriesMenuComponent implements OnInit { export class CategoriesMenuComponent implements OnInit {
readonly #sideMenuService = inject(SideMenuService); private sideMenuService = inject(SideMenuService);
categoryClicked = output<void>(); @Output()
categoryClicked = new EventEmitter<void>();
ngOnInit(): void { ngOnInit(): void {
this.#sideMenuService.loadCategories(); this.sideMenuService.loadCategories();
} }
get categories(): Signal<DisplayableCategory[]> { get categories$(): Observable<DisplayableCategory[]> {
return this.#sideMenuService.categories; return this.sideMenuService.categories$;
} }
setOpened(category: DisplayableCategory): void { setOpenned(category: DisplayableCategory): void {
if (category.isOpenned) { if (category.isOpenned) {
const categoryDiv = document.getElementById(`category-${category.id}`); const categoryDiv = document.getElementById(`category-${category.id}`);
if (categoryDiv) { if (categoryDiv) {
this.#closeAccordion(categoryDiv); this.closeAccordion(categoryDiv);
} }
} else { } else {
const categoriesDivs = document.getElementsByClassName('category-header'); const categoriesDivs = document.getElementsByClassName('category-header');
Array.from(categoriesDivs) Array.from(categoriesDivs)
.map(category => category as HTMLElement) .map(category => category as HTMLElement)
.forEach(categoryDiv => this.#closeAccordion(categoryDiv)); .forEach(categoryDiv => this.closeAccordion(categoryDiv));
const categoryDiv = document.getElementById(`category-${category.id}`); const categoryDiv = document.getElementById(`category-${category.id}`);
if (categoryDiv) { if (categoryDiv) {
this.#openAccordion(categoryDiv); this.openAccordion(categoryDiv);
} }
} }
this.#sideMenuService.setOpened(category); this.sideMenuService.setOpenned(category);
} }
#closeAccordion(categoryDiv: HTMLElement): void { private closeAccordion(categoryDiv: HTMLElement): void {
const divContent = categoryDiv?.nextElementSibling as HTMLElement; const divContent = categoryDiv?.nextElementSibling as HTMLElement;
divContent.style.maxHeight = '0'; divContent.style.maxHeight = '0';
} }
#openAccordion(categoryDiv: HTMLElement): void { private openAccordion(categoryDiv: HTMLElement): void {
const divContent = categoryDiv?.nextElementSibling as HTMLElement; const divContent = categoryDiv?.nextElementSibling as HTMLElement;
divContent.style.maxHeight = `${divContent.scrollHeight}px`; divContent.style.maxHeight = `${divContent.scrollHeight}px`;
} }
@@ -14,6 +14,6 @@
</button> </button>
</h1> </h1>
<h2 i18n>Categories</h2> <h2 i18n>Categories</h2>
<app-categories-menu (categoryClicked)="close()"/> <app-categories-menu (categoryClicked)="close()"></app-categories-menu>
</div> </div>
<div class="overlay {{ isOpened() ? 'displayed' : ''}}" (click)="close()"></div> <div class="overlay {{ isOpened() ? 'displayed' : ''}}" (click)="close()"></div>
@@ -1,5 +1,6 @@
import {inject, Injectable, Signal, signal} from '@angular/core'; import {inject, Injectable} from '@angular/core';
import {MatSnackBar} from '@angular/material/snack-bar'; import {MatSnackBar} from '@angular/material/snack-bar';
import {BehaviorSubject, Observable} from 'rxjs';
import {CategoryRestService} from '../../core/rest-services/category/category.rest-service'; import {CategoryRestService} from '../../core/rest-services/category/category.rest-service';
import {Category} from '../../core/rest-services/category/model/category'; import {Category} from '../../core/rest-services/category/model/category';
@@ -19,60 +20,68 @@ export interface DisplayableSubCategory {
providedIn: 'root' providedIn: 'root'
}) })
export class SideMenuService { export class SideMenuService {
readonly #categoryRestService = inject(CategoryRestService); private categoryRestService = inject(CategoryRestService);
readonly #snackBar = inject(MatSnackBar); private snackBar = inject(MatSnackBar);
#categories = signal<DisplayableCategory[]>([]); private categoriesSubject = new BehaviorSubject<DisplayableCategory[]>([]);
#isLoading = signal(false); private isLoadingSubject = new BehaviorSubject<boolean>(false);
isLoaded = signal(false); private isLoadedSubject = new BehaviorSubject<boolean>(false);
#mapToDisplayableCategory(category: Category): DisplayableCategory { private mapToDisplayableCategory(category: Category): DisplayableCategory {
return { return {
id: category.id, id: category.id,
name: category.name, name: category.name,
subCategories: category.subCategories.map(subCategory => this.#mapToDisplayableSubCategory(subCategory)), subCategories: category.subCategories.map(subCategory => this.mapToDisplayableSubCategory(subCategory)),
isOpenned: false isOpenned: false
}; };
} }
#mapToDisplayableSubCategory(subCategory: Category): DisplayableSubCategory { private mapToDisplayableSubCategory(subCategory: Category): DisplayableSubCategory {
return { return {
id: subCategory.id, id: subCategory.id,
name: subCategory.name name: subCategory.name
} }
} }
get categories(): Signal<DisplayableCategory[]> { private get categories(): DisplayableCategory[] {
return this.#categories.asReadonly(); return this.categoriesSubject.value;
} }
get isLoading(): Signal<boolean> { private save(categories: DisplayableCategory[]): void {
return this.#isLoading.asReadonly(); this.categoriesSubject.next(categories);
}
get categories$(): Observable<DisplayableCategory[]> {
return this.categoriesSubject.asObservable();
}
get isLoading$(): Observable<boolean> {
return this.isLoadingSubject.asObservable();
} }
loadCategories(): void { loadCategories(): void {
this.#isLoading.set(true); this.isLoadingSubject.next(true);
this.isLoaded.set(false); this.isLoadedSubject.next(false);
this.#categoryRestService.getCategories() this.categoryRestService.getCategories()
.then(categories => { .then(categories => {
const displayableCategories = categories const displayableCategories = categories
.filter(category => category.subCategories?.length) .filter(category => category.subCategories?.length)
.map(category => this.#mapToDisplayableCategory(category)); .map(category => this.mapToDisplayableCategory(category));
this.#categories.set(displayableCategories); this.categoriesSubject.next(displayableCategories);
}) })
.catch(error => { .catch(error => {
const errorMessage = $localize`An error occured while loading categories.`; const errorMessage = $localize`An error occured while loading categories.`;
console.error(errorMessage, error); console.error(errorMessage, error);
this.#snackBar.open(errorMessage, $localize`Close`, {duration: 5000}); this.snackBar.open(errorMessage, $localize`Close`, {duration: 5000});
}) })
.finally(() => { .finally(() => {
this.#isLoading.set(false); this.isLoadingSubject.next(false);
this.isLoaded.set(true); this.isLoadedSubject.next(true);
}); });
} }
setOpened(category: DisplayableCategory): void { setOpenned(category: DisplayableCategory): void {
const categories = this.#categories(); const categories = this.categories;
const matchingCategory = categories.find(categoryTemp => categoryTemp.id === category.id); const matchingCategory = categories.find(categoryTemp => categoryTemp.id === category.id);
if (matchingCategory) { if (matchingCategory) {
const actualOpennedCategory = categories.find(category => category.isOpenned); const actualOpennedCategory = categories.find(category => category.isOpenned);
@@ -82,7 +91,7 @@ export class SideMenuService {
categories.forEach(categoryTemp => categoryTemp.isOpenned = false); categories.forEach(categoryTemp => categoryTemp.isOpenned = false);
matchingCategory.isOpenned = true; matchingCategory.isOpenned = true;
} }
this.#categories.set(categories); this.save(categories);
} }
} }
} }