58 Commits

Author SHA1 Message Date
820757c836 fix: corrected formatter error
All checks were successful
Format / formatting (push) Successful in 7s
CI / build (push) Successful in 11s
2025-02-18 16:59:19 +01:00
730aa5f450 Merge remote-tracking branch 'refs/remotes/origin/back-postgres' into back-postgres
Some checks failed
Format / formatting (push) Failing after 7s
CI / build (push) Successful in 11s
2025-02-18 16:56:26 +01:00
dda3e5fcfd fix: most likely fixed merge conflict 2025-02-18 16:55:08 +01:00
5e8e875a37 feat: added user deletion and custom api call in the frontend
All checks were successful
CI / build (push) Successful in 11s
2025-02-18 16:45:41 +01:00
86e7dc7c75 fix: removed the test file that was causing the linter to fail
All checks were successful
CI / build (push) Successful in 12s
2025-02-18 16:37:55 +01:00
6235fe7e68 feat: separated class definition
Some checks failed
CI / build (push) Failing after 8s
2025-02-18 12:07:07 +01:00
fc73293122 fix: merge
All checks were successful
Format / formatting (push) Successful in 8s
CI / build (push) Successful in 13s
2025-02-12 19:03:39 +01:00
e26f8da662 fix: inserting data in db 2025-02-12 18:51:27 +01:00
b4c05f8c59 fix: formatting again
All checks were successful
Format / formatting (push) Successful in 8s
CI / build (push) Successful in 13s
2025-02-12 15:38:55 +01:00
ca282378ec fix: changed the formatting ?
Some checks failed
Format / formatting (push) Failing after 7s
CI / build (push) Successful in 13s
2025-02-12 15:34:16 +01:00
746fa97cf3 fix: applied formatter (on all import as well :)
Some checks failed
Format / formatting (push) Failing after 8s
CI / build (push) Successful in 13s
2025-02-12 15:29:12 +01:00
8b24456b48 feat: back to google as I can't make the other one work
Some checks failed
Format / formatting (push) Failing after 9s
CI / build (push) Successful in 13s
2025-02-12 15:14:48 +01:00
6befd10735 test: syntax validation
Some checks failed
Format / formatting (push) Failing after 1m24s
CI / build (push) Successful in 16s
2025-02-12 15:10:59 +01:00
db094a8d86 test: syntax validataion
Some checks failed
Format / formatting (push) Failing after 0s
CI / build (push) Successful in 12s
2025-02-12 15:09:42 +01:00
c739b4d26d test: syntax validataion
Some checks failed
Format / formatting (push) Failing after 3s
CI / build (push) Successful in 13s
2025-02-12 15:07:36 +01:00
dacb0dd179 fix: now uses another library
Some checks failed
CI / build (push) Successful in 52s
Format / formatting (push) Has been cancelled
2025-02-12 14:56:09 +01:00
b00c28a02a fix: now uses the correct version
Some checks failed
Format / formatting (push) Failing after 4s
CI / build (push) Successful in 12s
2025-02-12 14:54:27 +01:00
208cbbfa1d feat: now uses intellij
Some checks failed
Format / formatting (push) Failing after 3s
CI / build (push) Successful in 11s
2025-02-12 14:51:37 +01:00
d77f38b405 fix: removed exposed ports on the frontend
Some checks failed
Format / formatting (push) Failing after 7s
CI / build (push) Successful in 12s
2025-02-12 12:24:15 +01:00
525f98a054 feat: new makefile option 2025-02-12 12:23:53 +01:00
43aadac503 feat: reflected changes of path change 2025-02-12 12:23:04 +01:00
07f66f65ed feat: comments and security comfiguration improved. 2025-02-12 12:04:59 +01:00
6e5651c527 fix: remove dialect to supress a warning 2025-02-12 12:04:19 +01:00
1ed976b039 fix: now only show the incorrect files
Some checks failed
Format / formatting (push) Failing after 7s
CI / build (push) Successful in 12s
2025-02-12 11:42:16 +01:00
013b97cec0 test: check if the action is now red
Some checks failed
Format / formatting (push) Failing after 8s
CI / build (push) Successful in 12s
2025-02-12 11:34:57 +01:00
e7cb8cf469 feat: single .env file 2025-02-12 11:34:11 +01:00
184642a750 fix: coherent syntax 2025-02-12 11:32:41 +01:00
a8ae5f14d4 fix: db connection
All checks were successful
Format / formatting (push) Successful in 7s
CI / build (push) Successful in 12s
2025-02-12 11:09:17 +01:00
e6565275c8 fix: removed git action push to not destroy our history
All checks were successful
Format / formatting (push) Successful in 8s
CI / build (push) Successful in 12s
2025-02-12 11:04:49 +01:00
f629fb4a4e Google Java Format 2025-02-12 09:20:00 +00:00
7a9955b781 fix: merged
All checks were successful
Format / formatting (push) Successful in 9s
CI / build (push) Successful in 15s
2025-02-12 10:19:31 +01:00
1e97177777 fix: issue with foreign keys 2025-02-11 21:15:13 +01:00
9e2ab9fa5a Google Java Format 2025-02-11 18:27:36 +00:00
70b00a1996 feat: test on github validation
All checks were successful
Format / formatting (push) Successful in 8s
CI / build (push) Successful in 14s
2025-02-11 19:27:22 +01:00
720b19df93 wqMerge remote-tracking branch 'refs/remotes/origin/back-postgres' into back-postgres 2025-02-11 19:25:59 +01:00
1498b5908b fix: should now catch errors ? 2025-02-11 19:24:04 +01:00
45bbe51897 Google Java Format 2025-02-11 18:21:00 +00:00
c715955758 feat: the backend validator is better
All checks were successful
Format / formatting (push) Successful in 8s
CI / build (push) Successful in 13s
2025-02-11 19:20:49 +01:00
0fc4be2008 test: changed the verification
Some checks failed
Format / formatting (push) Failing after 44s
CI / build (push) Successful in 14s
2025-02-11 19:18:08 +01:00
c7ddc37bf9 test: changed the verification
Some checks failed
Format / formatting (push) Failing after 5s
CI / build (push) Successful in 14s
2025-02-11 19:12:49 +01:00
93bb46b017 test: check backend formatting
Some checks failed
check backend / formatting (push) Failing after 10s
CI / build (push) Successful in 12s
2025-02-11 19:04:02 +01:00
eed4e6f855 feat: multiple database and user in postgres
All checks were successful
CI / build (push) Successful in 11s
2025-02-11 11:07:45 +01:00
d2cc3e00e1 fix: Makefile now build correctly production environment 2025-02-11 10:07:00 +01:00
249d00177c feat: interraction between the backend and keycloak
Some checks failed
CI / build (push) Failing after 9s
2025-02-11 10:00:11 +01:00
36e4967394 Feat: first implementation of postgres db for backend
All checks were successful
CI / build (push) Successful in 13s
2025-02-11 00:08:53 +01:00
c32eea8a40 Merge pull request 'Mise en place d'un linter, de formatteur et d'actions afin de vérifier que le code du frontend compile bien' (#3) from linter into main
All checks were successful
CI / build (push) Successful in 12s
Reviewed-on: #3
Reviewed-by: Theo <tlelez@enseirb-matmeca.fr>
Vu que vous ne voulez pas faire avancer le projet, je vais abuser de mes droits d'administrateur et le faire.
2025-02-10 22:44:34 +01:00
30344a60b7 fix: removed import
All checks were successful
CI / build (push) Successful in 13s
2025-02-09 16:12:43 +01:00
2465545b6b fix: removed temp modal
Some checks failed
CI / build (push) Failing after 10s
2025-02-09 16:11:54 +01:00
83cbeb7a2e feat: single workflow that check prettier, linter and build
Some checks failed
CI / build (push) Failing after 11s
2025-02-09 16:07:40 +01:00
645a10477d feat: now respect codestyle
All checks were successful
CI / build (push) Successful in 4s
2025-02-09 15:59:30 +01:00
a859871265 fix: prettier now fail instead of doing nothing
Some checks failed
CI / build (push) Failing after 5s
2025-02-09 15:57:40 +01:00
0eab9a8063 feat: implemented prettier
All checks were successful
CI / build (push) Successful in 4s
2025-02-09 15:54:19 +01:00
1dff7573ff feat: now compliant with eslint
All checks were successful
CI / build (push) Successful in 10s
2025-02-09 15:28:09 +01:00
8af40bfe50 fix: linter action: installed required modules
Some checks failed
CI / build (push) Failing after 11s
2025-02-09 14:04:12 +01:00
afa4d34ec8 fix: linter action
Some checks failed
CI / build (push) Failing after 7s
2025-02-09 14:02:34 +01:00
c5fc5b600e fix: linter action
Some checks failed
CI / build (push) Failing after 12s
2025-02-09 14:00:49 +01:00
a4939737fe feat: trying to setup linter
Some checks failed
CI / build (push) Failing after 35s
2025-02-09 13:57:17 +01:00
e7ebcc0d3a feat: created eslint config 2025-02-09 12:36:43 +01:00
78 changed files with 3652 additions and 514 deletions

View File

@ -0,0 +1,18 @@
name: Format
on: [ push, pull_request ]
jobs:
formatting:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4 # v2 minimum required
- uses: actions/setup-java@v4
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21'
- uses: axel-op/googlejavaformat-action@v3
with:
args: "--set-exit-if-changed --skip-sorting-imports --aosp -n"

View File

@ -0,0 +1,24 @@
name: CI
on: push
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Install everything
working-directory: ./front/MyINPulse-front
run: npm i
- name: Run ESLint
working-directory: ./front/MyINPulse-front
run: npx eslint
- name: Run prettier
working-directory: ./front/MyINPulse-front
run: npx prettier src --check
- name: Build frontend
working-directory: ./front/MyINPulse-front
run: npm run build

1
.gitignore vendored
View File

@ -2,3 +2,4 @@
.idea
keycloak/CAS/target
docker-compose.yaml
postgres/data

View File

@ -1,13 +1,16 @@
help:
@echo "make [clean dev-front prod dev-back]"
@echo "make [clean dev-front prod dev-back dev]"
clean:
@cp config/frontdev.env front/MyINPulse-front/.env
@cp config/frontdev.env .env
@cp config/frontdev.env MyINPulse-back/.env
@cp config/prod.docker-compose.yaml docker-compose.yaml
@docker compose down
@rm -f docker-compose.yaml
@rm -f .env
@rm -f front/MyINPulse-front/.env
@rm -f MyINPulse-back/.env
# Install npm packages
front/MyINPulse-front/.installed:
@ -18,24 +21,37 @@ vite: ./front/MyINPulse-front/.installed
dev-front: clean vite
@cp config/frontdev.front.env front/MyINPulse-front/.env
@cp config/frontdev.main.env .env
@cp config/frontdev.env front/MyINPulse-front/.env
@cp config/frontdev.env .env
@cp config/frontdev.env MyINPulse-back/.env
@cp config/frontdev.docker-compose.yaml docker-compose.yaml
@docker compose up -d --build
@cd ./front/MyINPulse-front/ && npm run dev
prod: clean
@cp config/prod.front.env front/MyINPulse-front/.env
@cp config/prod.main.env .env
@cp config/frontdev.docker-compose.yaml docker-compose.yaml
@cp config/prod.env front/MyINPulse-front/.env
@cp config/prod.env .env
@cp config/prod.env .env
@cp config/prod.docker-compose.yaml docker-compose.yaml
@docker compose up -d --build
dev-back:
@cp config/backdev.front.env front/MyINPulse-front/.env
@cp config/backdev.main.env .env
@cp config/backdev.env front/MyINPulse-front/.env
@cp config/backdev.env .env
@cp config/backdev.env MyINPulse-back/.env
@cp config/backdev.docker-compose.yaml docker-compose.yaml
@docker compose up -d --build
@echo "cd MyINPulse-back"
@echo "cd MyINPulse-back" && echo 'export $$(cat .env | xargs)'
@echo "./gradlew bootRun --args='--server.port=8081'"
dev: clean vite
@cp config/dev.env front/MyINPulse-front/.env
@cp config/dev.env .env
@cp config/dev.env MyINPulse-back/.env
@cp config/dev.docker-compose.yaml docker-compose.yaml
@docker compose up -d --build
@echo "cd MyINPulse-back" && echo 'export $$(cat .env | xargs)'
@echo "./gradlew bootRun --args='--server.port=8081'"
@cd ./front/MyINPulse-front/ && npm run dev &

View File

@ -20,6 +20,10 @@ repositories {
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-data-rest'
implementation 'org.postgresql:postgresql'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

View File

@ -1,22 +1,10 @@
package enseirb.myinpulse;
import enseirb.myinpulse.security.KeycloakJwtRolesConverter;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.core.convert.converter.Converter;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.jwt.*;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import java.util.*;
import java.util.stream.Collectors;
import static org.springframework.security.authorization.AuthorityAuthorizationManager.hasRole;
@SpringBootApplication
public class MyinpulseApplication {
@ -24,6 +12,4 @@ public class MyinpulseApplication {
public static void main(String[] args) {
SpringApplication.run(MyinpulseApplication.class, args);
}
}

View File

@ -2,13 +2,14 @@ package enseirb.myinpulse.api;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.CrossOrigin;
import java.security.Principal;
import javax.management.relation.RoleNotFoundException;
@SpringBootApplication
@RestController
public class GetUserInfo {
@ -21,23 +22,19 @@ public class GetUserInfo {
}
@CrossOrigin(methods = {RequestMethod.GET, RequestMethod.OPTIONS})
@GetMapping("/random")
public boolean rand(){
System.err.println("HELLO");
@GetMapping("/unauth/random")
public boolean rand(@RequestHeader("Authorization") String token) throws RoleNotFoundException {
System.err.println(token);
return Math.random() > 0.5;
}
@CrossOrigin(methods = {RequestMethod.GET, RequestMethod.OPTIONS})
@GetMapping("/random2")
@GetMapping("/admin/random")
public boolean rand2() {
System.err.println("HELLO2");
return Math.random() > 0.5;
}
@CrossOrigin(methods = {RequestMethod.GET, RequestMethod.OPTIONS})
@GetMapping("/random3")
@GetMapping("/entrepreneur/random")
public boolean rand3() {
System.err.println("HELLO");
return Math.random() > 0.5;
}
}

View File

@ -1,6 +1,10 @@
package enseirb.myinpulse.config;
import static org.springframework.security.authorization.AuthorityAuthorizationManager.hasRole;
import enseirb.myinpulse.security.KeycloakJwtRolesConverter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
@ -12,39 +16,62 @@ import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import java.util.Arrays;
import java.util.List;
import static org.springframework.security.authorization.AuthorityAuthorizationManager.hasRole;
@Configuration
public class WebSecurityCustomConfiguration {
// CORS configuration
// TODO: make sure to only accept our own domains
@Value("${VITE_APP_URL}")
private String frontendUrl;
/**
* Configure the CORS (Cross Origin Ressource Sharing -- a security feature) configuration. The
* only allowed website is the frontend, defined in the .env file.
*
* @return the CORS configuration used by the backend
*/
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(List.of("*"));
configuration.setAllowedOrigins(List.of(frontendUrl));
configuration.setAllowedMethods(Arrays.asList("GET", "OPTIONS"));
configuration.setAllowedHeaders(Arrays.asList("authorization", "content-type",
"x-auth-token")); // Do not remove, this fixes the CORS errors when unauthenticated
UrlBasedCorsConfigurationSource source = new
UrlBasedCorsConfigurationSource();
configuration.setAllowedHeaders(
Arrays.asList("authorization", "content-type", "x-auth-token"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
/**
* Configure the authorisation required for each path.
*
* <p>admin endpoints are under /admin/* and entrepreneur are under /entrepreneur/*
*
* <p>If endpoints dont require authentication, they are under /unauth/
*
* @param http automatically filled in by spring.
* @return a securityfilterchain, automatically used by spring.
* @throws Exception TODO: figure out when the exception are raised
*/
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/random2").access(hasRole("REALM_MyINPulse-entrepreneur"))
.requestMatchers("/random").access(hasRole("REALM_MyINPulse-admin"))
.requestMatchers("/random3").permitAll()
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(jwt -> jwt.
jwtAuthenticationConverter(new KeycloakJwtRolesConverter())));
http.authorizeHttpRequests(
authorize ->
authorize
.requestMatchers("/entrepreneur/**")
.access(hasRole("REALM_MyINPulse-entrepreneur"))
.requestMatchers("/admin/**")
.access(hasRole("REALM_MyINPulse-admin"))
.requestMatchers("/unauth/**")
.permitAll()
.anyRequest()
.authenticated())
.oauth2ResourceServer(
oauth2 ->
oauth2.jwt(
jwt ->
jwt.jwtAuthenticationConverter(
new KeycloakJwtRolesConverter())));
return http.build();
}
}

View File

@ -0,0 +1,7 @@
package enseirb.myinpulse.exceptions;
public class RoleNotFoudException extends RuntimeException {
public RoleNotFoudException(String message) {
super(message);
}
}

View File

@ -0,0 +1,7 @@
package enseirb.myinpulse.exceptions;
public class UserNotFoundException extends RuntimeException {
public UserNotFoundException(String message) {
super(message);
}
}

View File

@ -0,0 +1,38 @@
package enseirb.myinpulse.postgres_db.controller;
import enseirb.myinpulse.postgres_db.model.Administrateurs;
import enseirb.myinpulse.postgres_db.repository.AdministrateursRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.server.ResponseStatusException;
import java.util.Optional;
@RestController
public class AdministrateursController {
@Autowired AdministrateursRepository administrateursRepository;
@GetMapping("/Administrateurs")
@ResponseBody
public Iterable<Administrateurs> allAdministrateurs() {
return this.administrateursRepository.findAll();
}
@GetMapping("/Administrateurs/{id}")
public Administrateurs getAdministrateursById(@PathVariable Long id) {
Optional<Administrateurs> administrateur = this.administrateursRepository.findById(id);
if (administrateur.isEmpty()) {
throw new ResponseStatusException(
HttpStatus.NOT_FOUND, "Cet administrateur n'existe pas");
}
return administrateur.get();
}
@PostMapping("/Administrateurs")
public Administrateurs addAdministrateurs(@RequestBody Administrateurs administrateurs) {
return this.administrateursRepository.save(administrateurs);
}
}

View File

@ -0,0 +1,49 @@
package enseirb.myinpulse.postgres_db.controller;
import enseirb.myinpulse.postgres_db.model.ComptesRendus;
import enseirb.myinpulse.postgres_db.repository.ComptesRendusRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.server.ResponseStatusException;
import java.util.Optional;
@RestController
public class ComptesRendusController {
@Autowired ComptesRendusRepository comptesRendusRepository;
@GetMapping("/ComptesRendus")
@ResponseBody
public Iterable<ComptesRendus> allComptesRendus() {
return this.comptesRendusRepository.findAll();
}
@GetMapping("/ComptesRendus/{id}")
public ComptesRendus getComptesRendusById(@PathVariable Long id) {
Optional<ComptesRendus> compteRendu = this.comptesRendusRepository.findById(id);
if (compteRendu.isEmpty()) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Ce compte rendu n'existe pas");
}
return compteRendu.get();
}
@PostMapping("/ComptesRendus")
public ComptesRendus addComptesRendus(@RequestBody ComptesRendus comptesRendus) {
return this.comptesRendusRepository.save(comptesRendus);
}
@PostMapping("/ComptesRendus/{id}")
public ComptesRendus updateProjets(@PathVariable Long id, String contenu_compte_rendu) {
Optional<ComptesRendus> compteRendu = this.comptesRendusRepository.findById(id);
if (compteRendu.isEmpty()) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Ce compte rendu n'existe pas");
}
if (contenu_compte_rendu != null) {
compteRendu.get().setContenu_compte_rendu(contenu_compte_rendu);
}
return this.comptesRendusRepository.save(compteRendu.get());
}
}

View File

@ -0,0 +1,58 @@
package enseirb.myinpulse.postgres_db.controller;
import enseirb.myinpulse.postgres_db.model.Entrepreneurs;
import enseirb.myinpulse.postgres_db.repository.EntrepreneursRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.server.ResponseStatusException;
import java.util.Optional;
@RestController
public class EntrepreneursController {
@Autowired EntrepreneursRepository entrepreneursRepository;
@GetMapping("/Entrepreneurs")
@ResponseBody
public Iterable<Entrepreneurs> allEntrepreneurs() {
return this.entrepreneursRepository.findAll();
}
@GetMapping("/Entrepreneurs/{id}")
public Entrepreneurs getEntrepreneursById(@PathVariable Long id) {
Optional<Entrepreneurs> entrepreneur = entrepreneursRepository.findById(id);
if (entrepreneur.isEmpty()) {
throw new ResponseStatusException(
HttpStatus.NOT_FOUND, "Cet entrepreneur n'existe pas");
}
return entrepreneur.get();
}
@PostMapping("/Entrepreneurs")
public Entrepreneurs addEntrepreneurs(@RequestBody Entrepreneurs entrepreneurs) {
return this.entrepreneursRepository.save(entrepreneurs);
}
@PostMapping("/Entrepreneurs/{id}")
public Entrepreneurs updateEntrepreneurs(
@PathVariable Long id, String ecole, String filiere, Boolean status_snee) {
Optional<Entrepreneurs> entrepreneur = entrepreneursRepository.findById(id);
if (entrepreneur.isEmpty()) {
throw new ResponseStatusException(
HttpStatus.NOT_FOUND, "Cet entrepreneur n'existe pas");
}
if (ecole != null) {
entrepreneur.get().setEcole(ecole);
}
if (filiere != null) {
entrepreneur.get().setFiliere(filiere);
}
if (status_snee != null) {
entrepreneur.get().setStatus_snee(status_snee);
}
return this.entrepreneursRepository.save(entrepreneur.get());
}
}

View File

@ -0,0 +1,64 @@
package enseirb.myinpulse.postgres_db.controller;
import enseirb.myinpulse.postgres_db.model.Projets;
import enseirb.myinpulse.postgres_db.repository.ProjetsRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.server.ResponseStatusException;
import java.time.LocalDate;
import java.util.Optional;
@RestController
public class ProjetsController {
@Autowired ProjetsRepository projetsRepository;
@GetMapping("/Projets")
@ResponseBody
public Iterable<Projets> allProjets() {
return this.projetsRepository.findAll();
}
@GetMapping("/Projets/{id}")
public Projets getProjetsById(@PathVariable Long id) {
Optional<Projets> projet = this.projetsRepository.findById(id);
if (projet.isEmpty()) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Ce projet n'existe pas");
}
return projet.get();
}
@PostMapping("/Projets")
public Projets addProjets(@RequestBody Projets projet) {
return this.projetsRepository.save(projet);
}
@PostMapping("/Projets/{id}")
public Projets updateProjets(
@PathVariable Long id,
String nom_projet,
byte[] logo,
LocalDate date_creation,
String status_projet) {
Optional<Projets> projet = this.projetsRepository.findById(id);
if (projet.isEmpty()) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Ce projet n'existe pas");
}
if (nom_projet != null) {
projet.get().setNom_projet(nom_projet);
}
if (logo != null) {
projet.get().setLogo(logo);
}
if (date_creation != null) {
projet.get().setDate_creation(date_creation);
}
if (status_projet != null) {
projet.get().setStatus_projet(status_projet);
}
return this.projetsRepository.save(projet.get());
}
}

View File

@ -0,0 +1,67 @@
package enseirb.myinpulse.postgres_db.controller;
import enseirb.myinpulse.postgres_db.model.RendezVous;
import enseirb.myinpulse.postgres_db.repository.RendezVousRepository;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.server.ResponseStatusException;
@RestController
public class RendezVousController {
@Autowired RendezVousRepository rendezVousRepository;
@GetMapping("/RendezVous")
@ResponseBody
public Iterable<RendezVous> allRendezVous() {
return this.rendezVousRepository.findAll();
}
@GetMapping("/RendezVous/{id}")
public RendezVous getRendezVousById(@PathVariable Long id) {
Optional<RendezVous> rendezVous = this.rendezVousRepository.findById(id);
if (rendezVous.isEmpty()) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Ce rendez vous n'existe pas");
}
return rendezVous.get();
}
@PostMapping("/RendezVous")
public RendezVous addRendezVous(@RequestBody RendezVous rendezVous) {
return this.rendezVousRepository.save(rendezVous);
}
@PostMapping("/RendezVous/{id}")
public RendezVous updateRendezVous(
@PathVariable Long id,
LocalDate date_rdv,
LocalTime heure_rdv,
LocalTime duree_rdv,
String lieu_rdv,
String sujet_rdv) {
Optional<RendezVous> rendezVous = this.rendezVousRepository.findById(id);
if (rendezVous.isEmpty()) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Ce rendez vous n'existe pas");
}
if (date_rdv != null) {
rendezVous.get().setDate_rdv(date_rdv);
}
if (heure_rdv != null) {
rendezVous.get().setHeure_rdv(heure_rdv);
}
if (duree_rdv != null) {
rendezVous.get().setDuree_rdv(duree_rdv);
}
if (lieu_rdv != null) {
rendezVous.get().setLieu_rdv(lieu_rdv);
}
if (sujet_rdv != null) {
rendezVous.get().setSujet_rdv(sujet_rdv);
}
return this.rendezVousRepository.save(rendezVous.get());
}
}

View File

@ -0,0 +1,60 @@
package enseirb.myinpulse.postgres_db.controller;
import enseirb.myinpulse.postgres_db.model.Sections;
import enseirb.myinpulse.postgres_db.repository.SectionsRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.server.ResponseStatusException;
import java.time.LocalDateTime;
import java.util.Optional;
@RestController
public class SectionsController {
@Autowired SectionsRepository sectionsRepository;
@GetMapping("/Sections")
@ResponseBody
public Iterable<Sections> allSections() {
return this.sectionsRepository.findAll();
}
@GetMapping("/Sections/{id}")
public Sections getSectionsById(@PathVariable Long id) {
Optional<Sections> section = this.sectionsRepository.findById(id);
if (section.isEmpty()) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Cette section n'extise pas");
}
return section.get();
}
@PostMapping("/Sections")
public Sections addSections(@RequestBody Sections sections) {
return this.sectionsRepository.save(sections);
}
@PostMapping("/Sections/{id}")
public Sections updateSections(
@PathVariable Long id,
String titre,
String contenu_section,
LocalDateTime date_modification) {
Optional<Sections> section = this.sectionsRepository.findById(id);
if (section.isEmpty()) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Cette section n'extise pas");
}
if (titre != null) {
section.get().setTitre(titre);
}
if (contenu_section != null) {
section.get().setContenu_section(contenu_section);
}
if (date_modification != null) {
section.get().setDate_modification(date_modification);
}
return this.sectionsRepository.save(section.get());
}
}

View File

@ -0,0 +1,67 @@
package enseirb.myinpulse.postgres_db.controller;
import enseirb.myinpulse.postgres_db.model.Utilisateurs;
import enseirb.myinpulse.postgres_db.repository.UtilisateursRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.server.ResponseStatusException;
import java.util.Optional;
@RestController
public class UtilisateursController {
@Autowired UtilisateursRepository utilisateursRepository;
@GetMapping("/Utilisateurs")
@ResponseBody
public Iterable<Utilisateurs> allUtilisateurs() {
return this.utilisateursRepository.findAll();
}
@GetMapping("/Utilisateurs/{id}")
public Utilisateurs getUtilisateursById(@PathVariable Long id) {
Optional<Utilisateurs> utilisateur = utilisateursRepository.findById(id);
if (utilisateur.isEmpty()) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Cet utilisateur n'existe pas");
}
return utilisateur.get();
}
@PostMapping("/Utilisateurs")
public Utilisateurs addUtilisateurs(@RequestBody Utilisateurs utilisateurs) {
return this.utilisateursRepository.save(utilisateurs);
}
@PostMapping("/Utilisateurs/{id}")
public Utilisateurs updateUtilisateurs(
@PathVariable Long id,
String nom_utilisateur,
String prenom_utilisateur,
String mail_principal,
String mail_secondaire,
String numero_telephone) {
Optional<Utilisateurs> utilisateur = utilisateursRepository.findById(id);
if (utilisateur.isEmpty()) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Cet utilisateur n'existe pas");
}
if (nom_utilisateur != null) {
utilisateur.get().setNom_utilisateur(nom_utilisateur);
}
if (prenom_utilisateur != null) {
utilisateur.get().setPrenom_utilisateur(prenom_utilisateur);
}
if (mail_principal != null) {
utilisateur.get().setMail_principal(mail_principal);
}
if (mail_secondaire != null) {
utilisateur.get().setMail_secondaire(mail_secondaire);
}
if (numero_telephone != null) {
utilisateur.get().setNumero_telephone(numero_telephone);
}
return this.utilisateursRepository.save(utilisateur.get());
}
}

View File

@ -0,0 +1,43 @@
package enseirb.myinpulse.postgres_db.model;
import jakarta.persistence.*;
import jakarta.persistence.PrimaryKeyJoinColumn;
import jakarta.persistence.Table;
import java.util.ArrayList;
import java.util.List;
@Entity
@Table(name = "administrateurs")
@PrimaryKeyJoinColumn(name = "id_administrateur", referencedColumnName = "id_utilisateur")
public class Administrateurs extends Utilisateurs {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "id_projet")
private Projets projetsAdministrateurs;
@OneToMany(mappedBy = "administrateursSections", fetch = FetchType.LAZY, orphanRemoval = true)
private List<Sections> ListSections = new ArrayList<>();
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "id_rdv")
private RendezVous rendezVousAdministrateurs;
public Administrateurs() {}
public Administrateurs(
String nom_utilisateur,
Long id_utilisateur,
String prenom_utilisateur,
String mail_principal,
String mail_secondaire,
String numero_telephone) {
super(
nom_utilisateur,
id_utilisateur,
prenom_utilisateur,
mail_principal,
mail_secondaire,
numero_telephone);
}
}

View File

@ -0,0 +1,46 @@
package enseirb.myinpulse.postgres_db.model;
import jakarta.persistence.*;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotNull;
@Entity
@Table(name = "comptes_rendus")
public class ComptesRendus {
@Id
@NotNull
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id_compte_rendu;
private String contenu_compte_rendu;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "id_rdv")
private RendezVous rendezVousComptesRendus;
public ComptesRendus() {}
public ComptesRendus(Long id_compte_rendu, String contenu_compte_rendu) {
this.id_compte_rendu = id_compte_rendu;
this.contenu_compte_rendu = contenu_compte_rendu;
}
public Long getId_compte_rendu() {
return id_compte_rendu;
}
public void setId_compte_rendu(Long id_compte_rendu) {
this.id_compte_rendu = id_compte_rendu;
}
public String getContenu_compte_rendu() {
return contenu_compte_rendu;
}
public void setContenu_compte_rendu(String contenu_compte_rendu) {
this.contenu_compte_rendu = contenu_compte_rendu;
}
}

View File

@ -0,0 +1,80 @@
package enseirb.myinpulse.postgres_db.model;
import jakarta.persistence.*;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
@Entity
@Table(name = "entrepreneurs")
@PrimaryKeyJoinColumn(name = "id_entrepreneur", referencedColumnName = "id_utilisateur")
public class Entrepreneurs extends Utilisateurs {
@Column(length = 255)
private String ecole;
@Column(length = 255)
private String filiere;
private boolean status_snee;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "id_projet_participation", referencedColumnName = "id_projet")
private Projets projetsParticipation;
// @Column(insertable=false, updatable=false)
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "id_projet_propose", referencedColumnName = "id_projet")
private Projets projetsPropose;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "id_rdv")
private RendezVous rendezVousEntrepreneurs;
public Entrepreneurs() {}
public Entrepreneurs(
String nom_utilisateur,
Long id_utilisateur,
String prenom_utilisateur,
String mail_principal,
String mail_secondaire,
String numero_telephone,
String ecole,
boolean status_snee,
String filiere) {
super(
nom_utilisateur,
id_utilisateur,
prenom_utilisateur,
mail_principal,
mail_secondaire,
numero_telephone);
this.ecole = ecole;
this.status_snee = status_snee;
this.filiere = filiere;
}
public String getEcole() {
return ecole;
}
public void setEcole(String ecole) {
this.ecole = ecole;
}
public String getFiliere() {
return filiere;
}
public void setFiliere(String filiere) {
this.filiere = filiere;
}
public boolean isStatus_snee() {
return status_snee;
}
public void setStatus_snee(boolean status_snee) {
this.status_snee = status_snee;
}
}

View File

@ -0,0 +1,98 @@
package enseirb.myinpulse.postgres_db.model;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
@Entity
@Table(name = "projets")
public class Projets {
@Id
@NotNull
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id_projet;
@Column(length = 255)
private String nom_projet;
private byte[] logo;
private LocalDate date_creation;
@Column(length = 255)
private String status_projet;
@OneToMany(mappedBy = "projetsAdministrateurs", fetch = FetchType.LAZY, orphanRemoval = true)
private List<Administrateurs> listAdministrateurs = new ArrayList<>();
@OneToMany(mappedBy = "projetsParticipation", fetch = FetchType.LAZY, orphanRemoval = true)
private List<Entrepreneurs> ListEntrepreneursParticipation = new ArrayList<>();
@OneToOne(mappedBy = "projetsPropose", fetch = FetchType.LAZY, orphanRemoval = true)
private Entrepreneurs entrepreneursPropose;
@OneToMany(mappedBy = "projetsSections", fetch = FetchType.LAZY, orphanRemoval = true)
private List<Sections> ListSections = new ArrayList<>();
// Hibernate expects entities to have a no-arg constructor,
// though it does not necessarily have to be public.
public Projets() {}
public Projets(
Long id_projet,
String nom_projet,
byte[] logo,
LocalDate date_creation,
String status_projet) {
this.id_projet = id_projet;
this.nom_projet = nom_projet;
this.logo = logo;
this.date_creation = date_creation;
this.status_projet = status_projet;
}
public Long getId_projet() {
return id_projet;
}
public void setId_projet(Long id_projet) {
this.id_projet = id_projet;
}
public String getNom_projet() {
return nom_projet;
}
public void setNom_projet(String nom_projet) {
this.nom_projet = nom_projet;
}
public byte[] getLogo() {
return logo;
}
public void setLogo(byte[] logo) {
this.logo = logo;
}
public LocalDate getDate_creation() {
return date_creation;
}
public void setDate_creation(LocalDate date_creation) {
this.date_creation = date_creation;
}
public String getStatus_projet() {
return status_projet;
}
public void setStatus_projet(String status_projet) {
this.status_projet = status_projet;
}
}

View File

@ -0,0 +1,111 @@
package enseirb.myinpulse.postgres_db.model;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.List;
@Entity
@Table(name = "rendez_vous")
public class RendezVous {
@OneToMany(mappedBy = "rendezVousEntrepreneurs", fetch = FetchType.LAZY, orphanRemoval = true)
private final List<Entrepreneurs> ListEntrepreneurs = new ArrayList<>();
@OneToMany(mappedBy = "rendezVousAdministrateurs", fetch = FetchType.LAZY, orphanRemoval = true)
private final List<Administrateurs> ListAdministrateurs = new ArrayList<>();
@OneToMany(mappedBy = "rendezVousComptesRendus", fetch = FetchType.LAZY, orphanRemoval = true)
private final List<ComptesRendus> ListComptesRendus = new ArrayList<>();
@ManyToMany(
fetch = FetchType.LAZY,
cascade = {CascadeType.ALL})
@JoinTable(
name = "concerner",
joinColumns = @JoinColumn(name = "id_rdv"),
inverseJoinColumns = @JoinColumn(name = "id_section"))
List<Sections> ListSections = new ArrayList<>();
@Id
@NotNull
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id_rdv;
private LocalDate date_rdv;
private LocalTime heure_rdv;
private LocalTime duree_rdv;
@Column(length = 255)
private String lieu_rdv;
private String sujet_rdv;
public RendezVous() {}
public RendezVous(
Long id_rdv,
LocalDate date_rdv,
LocalTime heure_rdv,
LocalTime duree_rdv,
String lieu_rdv,
String sujet_rdv) {
this.id_rdv = id_rdv;
this.date_rdv = date_rdv;
this.heure_rdv = heure_rdv;
this.duree_rdv = duree_rdv;
this.lieu_rdv = lieu_rdv;
this.sujet_rdv = sujet_rdv;
}
public Long getId_rdv() {
return id_rdv;
}
public void setId_rdv(Long id_rdv) {
this.id_rdv = id_rdv;
}
public LocalDate getDate_rdv() {
return date_rdv;
}
public void setDate_rdv(LocalDate date_rdv) {
this.date_rdv = date_rdv;
}
public LocalTime getHeure_rdv() {
return heure_rdv;
}
public void setHeure_rdv(LocalTime heure_rdv) {
this.heure_rdv = heure_rdv;
}
public LocalTime getDuree_rdv() {
return duree_rdv;
}
public void setDuree_rdv(LocalTime duree_rdv) {
this.duree_rdv = duree_rdv;
}
public String getLieu_rdv() {
return lieu_rdv;
}
public void setLieu_rdv(String lieu_rdv) {
this.lieu_rdv = lieu_rdv;
}
public String getSujet_rdv() {
return sujet_rdv;
}
public void setSujet_rdv(String sujet_rdv) {
this.sujet_rdv = sujet_rdv;
}
}

View File

@ -0,0 +1,81 @@
package enseirb.myinpulse.postgres_db.model;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@Entity
@Table(name = "sections")
public class Sections {
@Id
@NotNull
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id_section;
@Column(length = 255)
private String titre;
private String contenu_section;
private LocalDateTime date_modification;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "id_projet")
private Projets projetsSections;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "id_admnistrateur")
private Administrateurs administrateursSections;
@ManyToMany(mappedBy = "ListSections")
private List<RendezVous> rendezVous = new ArrayList<>();
public Sections() {}
public Sections(
Long id_section,
String titre,
String contenu_section,
LocalDateTime date_modification) {
this.id_section = id_section;
this.titre = titre;
this.contenu_section = contenu_section;
this.date_modification = date_modification;
}
public String getTitre() {
return titre;
}
public void setTitre(String titre) {
this.titre = titre;
}
public Long getId_section() {
return id_section;
}
public void setId_section(Long id_section) {
this.id_section = id_section;
}
public String getContenu_section() {
return contenu_section;
}
public void setContenu_section(String contenu_section) {
this.contenu_section = contenu_section;
}
public LocalDateTime getDate_modification() {
return date_modification;
}
public void setDate_modification(LocalDateTime date_modification) {
this.date_modification = date_modification;
}
}

View File

@ -0,0 +1,95 @@
package enseirb.myinpulse.postgres_db.model;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
@Entity
@Table(name = "utilisateurs")
@Inheritance(strategy = InheritanceType.JOINED)
public class Utilisateurs {
@Id
@NotNull
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id_utilisateur;
@Column(length = 255)
private String nom_utilisateur;
@Column(length = 255)
private String prenom_utilisateur;
@Column(length = 255)
private String mail_principal;
@Column(length = 255)
private String mail_secondaire;
@Column(length = 20)
private String numero_telephone;
public Utilisateurs() {}
public Utilisateurs(
String nom_utilisateur,
Long id_utilisateur,
String prenom_utilisateur,
String mail_principal,
String mail_secondaire,
String numero_telephone) {
this.nom_utilisateur = nom_utilisateur;
this.id_utilisateur = id_utilisateur;
this.prenom_utilisateur = prenom_utilisateur;
this.mail_principal = mail_principal;
this.mail_secondaire = mail_secondaire;
this.numero_telephone = numero_telephone;
}
public Long getId_utilisateur() {
return id_utilisateur;
}
public void setId_utilisateur(Long id_utilisateur) {
this.id_utilisateur = id_utilisateur;
}
public String getNom_utilisateur() {
return nom_utilisateur;
}
public void setNom_utilisateur(String nom_utilisateur) {
this.nom_utilisateur = nom_utilisateur;
}
public String getPrenom_utilisateur() {
return prenom_utilisateur;
}
public void setPrenom_utilisateur(String prenom_utilisateur) {
this.prenom_utilisateur = prenom_utilisateur;
}
public String getMail_principal() {
return mail_principal;
}
public void setMail_principal(String mail_principal) {
this.mail_principal = mail_principal;
}
public String getMail_secondaire() {
return mail_secondaire;
}
public void setMail_secondaire(String mail_secondaire) {
this.mail_secondaire = mail_secondaire;
}
public String getNumero_telephone() {
return numero_telephone;
}
public void setNumero_telephone(String numero_telephone) {
this.numero_telephone = numero_telephone;
}
}

View File

@ -0,0 +1,14 @@
package enseirb.myinpulse.postgres_db.repository;
import enseirb.myinpulse.postgres_db.model.Administrateurs;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
@RepositoryRestResource
public interface AdministrateursRepository extends JpaRepository<Administrateurs, Long> {
/* @Query("SELECT a from Administrateurs a")
Administrateurs findAllAdministrateurs(); */
}

View File

@ -0,0 +1,9 @@
package enseirb.myinpulse.postgres_db.repository;
import enseirb.myinpulse.postgres_db.model.ComptesRendus;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
@RepositoryRestResource
public interface ComptesRendusRepository extends JpaRepository<ComptesRendus, Long> {}

View File

@ -0,0 +1,14 @@
package enseirb.myinpulse.postgres_db.repository;
import enseirb.myinpulse.postgres_db.model.Entrepreneurs;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
@RepositoryRestResource
public interface EntrepreneursRepository extends JpaRepository<Entrepreneurs, Long> {
/* @Query("SELECT e from Entrepreneurs e")
Entrepreneurs findAllEntrepreneurs(); */
}

View File

@ -0,0 +1,9 @@
package enseirb.myinpulse.postgres_db.repository;
import enseirb.myinpulse.postgres_db.model.Projets;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
@RepositoryRestResource
public interface ProjetsRepository extends JpaRepository<Projets, Long> {}

View File

@ -0,0 +1,9 @@
package enseirb.myinpulse.postgres_db.repository;
import enseirb.myinpulse.postgres_db.model.RendezVous;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
@RepositoryRestResource
public interface RendezVousRepository extends JpaRepository<RendezVous, Long> {}

View File

@ -0,0 +1,9 @@
package enseirb.myinpulse.postgres_db.repository;
import enseirb.myinpulse.postgres_db.model.Sections;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
@RepositoryRestResource
public interface SectionsRepository extends JpaRepository<Sections, Long> {}

View File

@ -0,0 +1,14 @@
package enseirb.myinpulse.postgres_db.repository;
import enseirb.myinpulse.postgres_db.model.Utilisateurs;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
@RepositoryRestResource
public interface UtilisateursRepository extends JpaRepository<Utilisateurs, Long> {
/* @Query("SELECT u from Utilisateurs u")
Utilisateurs findAllUtilisateurs(); */
}

View File

@ -1,5 +1,11 @@
/*
* Source: https://github.com/ChristianHuff-DEV/secure-spring-rest-api-using-keycloak/blob/main/src/main/java/io/betweendata/RestApi/security/oauth2/KeycloakJwtRolesConverter.java
* edited by Pierre Tellier
*/
package enseirb.myinpulse.security;
import static java.util.stream.Collectors.toSet;
import org.springframework.core.convert.converter.Converter;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
@ -14,44 +20,37 @@ import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static java.util.stream.Collectors.toSet;
public class KeycloakJwtRolesConverter implements Converter<Jwt, AbstractAuthenticationToken> {
/**
* Prefix used for realm level roles.
*/
/** Prefix used for realm level roles. */
public static final String PREFIX_REALM_ROLE = "ROLE_REALM_";
/**
* Prefix used in combination with the resource (client) name for resource level roles.
*/
/** Prefix used in combination with the resource (client) name for resource level roles. */
public static final String PREFIX_RESOURCE_ROLE = "ROLE_";
/**
* Name of the claim containing the realm level roles
*/
/** Name of the claim containing the realm level roles */
private static final String CLAIM_REALM_ACCESS = "realm_access";
/**
* Name of the claim containing the resources (clients) the user has access to.
*/
/** Name of the claim containing the resources (clients) the user has access to. */
private static final String CLAIM_RESOURCE_ACCESS = "resource_access";
/**
* Name of the claim containing roles. (Applicable to realm and resource level.)
*/
/** Name of the claim containing roles. (Applicable to realm and resource level.) */
private static final String CLAIM_ROLES = "roles";
@Override
public AbstractAuthenticationToken convert(Jwt source)
{
return new JwtAuthenticationToken(source, Stream.concat(new JwtGrantedAuthoritiesConverter().convert(source)
.stream(), TEMPORARNAME(source).stream())
public AbstractAuthenticationToken convert(Jwt source) {
return new JwtAuthenticationToken(
source,
Stream.concat(
new JwtGrantedAuthoritiesConverter().convert(source).stream(),
tokenRolesExtractor(source).stream())
.collect(toSet()));
}
/**
* Extracts the realm and resource level roles from a JWT token distinguishing between them using prefixes.
* Extracts the realm and resource level roles from a JWT token distinguishing between them
* using prefixes.
*/
public Collection<GrantedAuthority> TEMPORARNAME(Jwt jwt) {
public Collection<GrantedAuthority> tokenRolesExtractor(Jwt jwt) {
// Collection that will hold the extracted roles
Collection<GrantedAuthority> grantedAuthorities = new ArrayList<>();
@ -66,7 +65,8 @@ public class KeycloakJwtRolesConverter implements Converter<Jwt, AbstractAuthent
// Check if any roles are present
if (roles != null && !roles.isEmpty()) {
// Iterate of the roles and add them to the granted authorities
Collection<GrantedAuthority> realmRoles = roles.stream()
Collection<GrantedAuthority> realmRoles =
roles.stream()
// Prefix all realm roles with "ROLE_realm_"
.map(role -> new SimpleGrantedAuthority(PREFIX_REALM_ROLE + role))
.collect(Collectors.toList());
@ -75,24 +75,33 @@ public class KeycloakJwtRolesConverter implements Converter<Jwt, AbstractAuthent
}
// Resource (client) roles
// A user might have access to multiple resources all containing their own roles. Therefore, it is a map of
// A user might have access to multiple resources all containing their own roles. Therefore,
// it is a map of
// resource each possibly containing a "roles" property.
Map<String, Map<String, Collection<String>>> resourceAccess = jwt.getClaim(CLAIM_RESOURCE_ACCESS);
Map<String, Map<String, Collection<String>>> resourceAccess =
jwt.getClaim(CLAIM_RESOURCE_ACCESS);
// Check if resources are assigned
if (resourceAccess != null && !resourceAccess.isEmpty()) {
// Iterate of all the resources
resourceAccess.forEach((resource, resourceClaims) -> {
resourceAccess.forEach(
(resource, resourceClaims) -> {
// Iterate of the "roles" claim inside the resource claims
resourceClaims.get(CLAIM_ROLES).forEach(
// Add the role to the granted authority prefixed with ROLE_ and the name of the resource
role -> grantedAuthorities.add(new SimpleGrantedAuthority(PREFIX_RESOURCE_ROLE + resource + "_" + role))
);
resourceClaims
.get(CLAIM_ROLES)
.forEach(
// Add the role to the granted authority prefixed with ROLE_
// and the name of the resource
role ->
grantedAuthorities.add(
new SimpleGrantedAuthority(
PREFIX_RESOURCE_ROLE
+ resource
+ "_"
+ role)));
});
}
return grantedAuthorities;
}
}

View File

@ -0,0 +1,135 @@
package enseirb.myinpulse.utils.keycloak;
import static org.springframework.http.MediaType.APPLICATION_JSON;
import enseirb.myinpulse.exceptions.UserNotFoundException;
import enseirb.myinpulse.utils.keycloak.datatypes.RoleRepresentation;
import enseirb.myinpulse.utils.keycloak.datatypes.UserRepresentation;
import org.springframework.web.client.RestClient;
import javax.management.relation.RoleNotFoundException;
public class KeycloakApi {
static final String keycloakUrl;
static final String realmName;
static {
if (System.getenv("VITE_KEYCLOAK_URL") == null) {
System.exit(-1);
}
keycloakUrl = System.getenv("VITE_KEYCLOAK_URL");
}
static {
if (System.getenv("VITE_KEYCLOAK_REALM") == null) {
System.exit(-1);
}
realmName = System.getenv("VITE_KEYCLOAK_REALM");
}
/**
* Uses Keycloak API to retrieve a role representation of a role by its name
*
* @param roleName name of the role
* @param bearer authorization header used by the client to authenticate to keycloak
*/
public static RoleRepresentation getRoleRepresentationByName(String roleName, String bearer)
throws RoleNotFoundException {
RoleRepresentation[] response =
RestClient.builder()
.baseUrl(keycloakUrl)
.defaultHeader("Authorization", bearer)
.build()
.get()
.uri("/admin/realms/{realmName}/roles/{roleName}", realmName, roleName)
.retrieve()
.body(RoleRepresentation[].class);
if (response == null || response.length == 0) {
throw new RoleNotFoundException("Role not found");
}
return response[0];
}
/**
* Use keycloak API to to retreive a userID via his name or email.
*
* @param username username or mail of the user
* @param bearer bearer of the user, allowing access to database
* @return the userid, as a String
* @throws UserNotFoundException
*/
public static String getUserIdByName(String username, String bearer)
throws UserNotFoundException {
UserRepresentation[] response =
RestClient.builder()
.baseUrl(keycloakUrl)
.defaultHeader("Authorization", bearer)
.build()
.get()
.uri(
"/admin/realms/{realmName}/users?username={username}",
realmName,
username)
.retrieve()
.body(UserRepresentation[].class);
if (response == null || response.length == 0) {
throw new UserNotFoundException("User not found");
}
return response[0].id;
}
/**
* TODO: check for error
*
* <p>Set a keycloak role to a keycloak user.
*
* <p>Usual roles should be `MyINPulse-admin` and `MyINPulse-entrepreneur`
*
* @param username
* @param roleName
* @param bearer
* @throws RoleNotFoundException
* @throws UserNotFoundException
*/
public static void setRoleToUser(String username, String roleName, String bearer)
throws RoleNotFoundException, UserNotFoundException {
RoleRepresentation roleRepresentation = getRoleRepresentationByName(roleName, bearer);
String userId = getUserIdByName(username, bearer);
RestClient.builder()
.baseUrl(keycloakUrl)
.defaultHeader("Authorization", bearer)
.build()
.post()
.uri(
"/admin/realms/${realmName}/users/${userId}/role-mappings/realm",
realmName,
userId)
.body(roleRepresentation)
.contentType(APPLICATION_JSON)
.retrieve();
}
/**
* Delete a user from Keycloak database. TODO: check the bearer permission.
*
* @param username
* @param bearer
* @throws UserNotFoundException
*/
public static void deleteUser(String username, String bearer) throws UserNotFoundException {
String userId = getUserIdByName(username, bearer);
RestClient.builder()
.baseUrl(keycloakUrl)
.defaultHeader("Authorization", bearer)
.build()
.delete()
.uri("/admin/realms/${realmName}/users/${userId}", realmName, userId)
.retrieve();
}
}

View File

@ -0,0 +1,7 @@
package enseirb.myinpulse.utils.keycloak.datatypes;
public class RoleRepresentation {
public String id;
public String name;
public String description;
}

View File

@ -0,0 +1,6 @@
package enseirb.myinpulse.utils.keycloak.datatypes;
public class UserRepresentation {
public String id;
public String name;
}

View File

@ -2,3 +2,7 @@ spring.application.name=myinpulse
spring.security.oauth2.resourceserver.jwt.jwk-set-uri=http://localhost:7080/realms/test/protocol/openid-connect/certs
spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:7080/realms/test
logging.level.org.springframework.security=DEBUG
spring.datasource.url=jdbc:postgresql://${DATABASE_URL}/${BACKEND_DB}
spring.datasource.username=${BACKEND_USER}
spring.datasource.password=${BACKEND_PASSWORD}
spring.jpa.hibernate.ddl-auto=update

View File

@ -0,0 +1,68 @@
TRUNCATE projets, utilisateurs, entrepreneurs, sections, rendez_vous, comptes_rendus CASCADE;
INSERT INTO projets (nom_projet, logo, date_creation, status_projet)
VALUES ('Eau du robinet', decode('013d7d16d7ad4fefb61bd95b765c8ceb', 'hex'), TO_DATE('01-OCT-2023', 'DD-MON-YYYY'),
'En cours'),
('Air oxygéné', decode('150647a0984e8f228cd14b54', 'hex'), TO_DATE('04-APR-2024', 'DD-MON-YYYY'), 'En cours'),
('Débat concours', decode('022024abd5486e245c145dda65116f', 'hex'), TO_DATE('22-NOV-2023', 'DD-MON-YYYY'),
'Suspendu'),
('HDeirbMI', decode('ab548d6c1d595a2975e6476f544d14c55a', 'hex'), TO_DATE('07-DEC-2024', 'DD-MON-YYYY'),
'Lancement');
INSERT INTO utilisateurs (nom_utilisateur, prenom_utilisateur, mail_principal, mail_secondaire, numero_telephone) VALUES
('Dupont', 'Dupond', 'super@mail.fr', 'super2@mail.fr', '06 45 72 45 98'),
('Martin', 'Matin', 'genial@mail.fr', 'genial2@mail.fr', '06 52 14 58 73'),
('Charvet', 'Lautre', 'mieux@tmail.fr', 'mieux2@tmail.fr', '07 49 82 16 35'),
('Leguez', 'Theo', 'bof@mesmails.fr', 'bof2@mesmails.fr', '+33 6 78 14 25 29'),
('Kia', 'Bi', 'special@mail.fr', 'special2@mail.fr', '07 65 31 38 95'),
('Ducaillou', 'Pierre', 'maildefou@xyz.fr', 'maildefou2@xyz.fr', '06 54 78 12 62');
INSERT INTO entrepreneurs (ecole, filiere, status_snee, id_entrepreneur) VALUES
('ENSEIRB-MATMECA', 'INFO', TRUE, 1),
('ENSC', 'Cognitique', TRUE, 2),
('ENSEIRB-MATMECA', 'MATMECA', FALSE, 3),
('SupOptique', 'Classique', TRUE, 4),
('ENSEGID', 'Géoscience', FALSE, 5),
('ENSMAC', 'Matériaux composites - Mécanique', FALSE, 6);
INSERT INTO sections (titre, contenu_section, date_modification) VALUES
('Problème', 'les problèmes...', TO_TIMESTAMP('15-JAN-2025 09:30:20', 'DD-MON-YYYY, HH24:MI:SS')),
('Segment de client', 'Le segment AB passant le client n°8 est de longueur 32mm.
Le segment BC a quant à lui un longueur de 28mm. Quelle la longueur du segment AC ?', TO_TIMESTAMP('12-OCT-2022 17:47:38', 'DD-MON-YYYY, HH24:MI:SS')),
('Proposition de valeur unique', '''Son prix est de 2594€'' ''Ah oui c''est unique en effet', TO_TIMESTAMP('25-MAY-2024 11:12:04', 'DD-MON-YYYY, HH24:MI:SS')),
('Solution', 'Un problème ? Une solution', TO_TIMESTAMP('08-FEB-2024 10:17:53', 'DD-MON-YYYY, HH24:MI:SS')),
('Canaux', 'Ici nous avons la Seine, là-bas le Rhin, oh et plus loin le canal de Suez', TO_TIMESTAMP('19-JUL-2023 19:22:45', 'DD-MON-YYYY, HH24:MI:SS')),
('Sources de revenus', 'Y''en n''a pas on est pas payé. Enfin y''a du café quoi', TO_TIMESTAMP('12-JAN-2025 11:40:26', 'DD-MON-YYYY, HH24:MI:SS')),
('Structure des coûts', '''Ah oui là ça va faire au moins 1000€ par mois'', Eirbware', TO_TIMESTAMP('06-FEB-2025 13:04:06', 'DD-MON-YYYY, HH24:MI:SS')),
('Indicateurs clés', 'On apprend les clés comme des badges, ça se fait', TO_TIMESTAMP('05-FEB-2025 12:42:38', 'DD-MON-YYYY, HH24:MI:SS')),
('Avantages concurrentiel', 'On est meilleur', TO_TIMESTAMP('23-APR-2024 16:24:02', 'DD-MON-YYYY, HH24:MI:SS'));
INSERT INTO rendez_vous (date_rdv, heure_rdv, duree_rdv, lieu_rdv, sujet_rdv) VALUES
(TO_DATE('24-DEC-2023', 'DD-MON-YYYY'), '00:00:00', '00:37:53', 'À la maison', 'Ouvrir les cadeaux'),
(TO_DATE('15-AUG-2024', 'DD-MON-YYYY'), '22:35:00', '00:12:36', 'Sur les quais ou dans un champ probablement', 'BOUM BOUM les feux d''artifices (on fête quoi déjà ?)'),
(TO_DATE('28-FEB-2023', 'DD-MON-YYYY'), '14:20:00', '00:20:00', 'Salle TD 15', 'Ah mince c''est pas une année bissextile !'),
(TO_DATE('23-JAN-2024', 'DD-MON-YYYY'), '12:56:27', '11:03:33', 'Là où le vent nous porte', 'Journée la plus importante de l''année'),
(TO_DATE('25-AUG-2025', 'DD-MON-YYYY'), '00:09:00', '01:00:00', 'Euh c''est par où l''amphi 56 ?', 'Rentrée scolaire (il fait trop froid c''est quoi ça on est en août)');
INSERT INTO comptes_rendus (contenu_compte_rendu) VALUES
('Ah oui ça c''est super, ah ouais j''aime bien, bien vu de penser à ça'),
('Bonne réunion'),
('Ouais, j''ai rien compris mais niquel on fait comme vous avez dit'),
('Non non ça va pas du tout ce que tu me proposes, faut tout refaire'),
('Réponse de la DSI : non'),
('Trop dommage qu''Apple ait sorti leur logiciel avant nous, on avait la même idée et tout on aurait tellement pu leur faire de la concurrence');

View File

@ -0,0 +1 @@
DROP TABLE IF EXISTS administrateurs, projets, utilisateurs, entrepreneurs, sections, rendez_vous, comptes_rendus, concerner CASCADE;

View File

@ -0,0 +1,134 @@
DROP TABLE IF EXISTS projets CASCADE;
DROP TABLE IF EXISTS utilisateurs CASCADE;
DROP TABLE IF EXISTS entrepreneurs CASCADE;
DROP TABLE IF EXISTS administrateurs CASCADE;
DROP TABLE IF EXISTS sections CASCADE;
DROP TABLE IF EXISTS rendez_vous CASCADE;
DROP TABLE IF EXISTS comptes_rendus CASCADE;
DROP TABLE IF EXISTS concerner CASCADE;
DROP TABLE IF EXISTS formes CASCADE;
CREATE TABLE projets
(
id_projet SERIAL NOT NULL,
nom_projet VARCHAR(255),
logo BYTEA,
date_creation DATE,
status_projet VARCHAR(255),
CONSTRAINT pk_projet PRIMARY KEY (id_projet)
);
CREATE TABLE utilisateurs
(
id_utilisateur SERIAL NOT NULL,
nom_utilisateur VARCHAR(255) ,
prenom_utilisateur VARCHAR(255) ,
mail_principal VARCHAR(255) ,
mail_secondaire VARCHAR(255) ,
numero_telephone VARCHAR(20) ,
CONSTRAINT pk_utilisateur PRIMARY KEY (id_utilisateur) );
CREATE TABLE entrepreneurs
(
id_entrepreneur SERIAL REFERENCES utilisateurs (id_utilisateur),
ecole VARCHAR(255),
filiere VARCHAR(255),
status_snee BOOLEAN,
CONSTRAINT pk_entrepreneur PRIMARY KEY (id_entrepreneur)
);
CREATE TABLE administrateurs
(
id_administrateur SERIAL REFERENCES utilisateurs (id_utilisateur),
CONSTRAINT pk_administrateur PRIMARY KEY (id_administrateur)
);
CREATE TABLE sections
(
id_section SERIAL NOT NULL,
titre VARCHAR(255),
contenu_section TEXT,
date_modification TIMESTAMP,
CONSTRAINT pk_section PRIMARY KEY (id_section)
);
CREATE TABLE rendez_vous
(
id_rdv SERIAL NOT NULL,
date_rdv DATE,
heure_rdv TIME,
duree_rdv TIME,
lieu_rdv VARCHAR(255),
sujet_rdv TEXT,
CONSTRAINT pk_rdv PRIMARY KEY (id_rdv)
);
CREATE TABLE comptes_rendus
(
id_compte_rendu SERIAL NOT NULL,
contenu_compte_rendu TEXT,
CONSTRAINT pk_compte_rendu PRIMARY KEY (id_compte_rendu)
);
CREATE TABLE concerner
(
id_section SERIAL REFERENCES sections (id_section),
id_rdv SERIAL REFERENCES sections (id_rdv),
CONSTRAINT pk_concerner PRIMARY KEY (id_section, id_rdv)
);
ALTER TABLE projets
ADD CONSTRAINT fk1_projet FOREIGN KEY (id_administrateur)
REFERENCES administrateurs (id_administrateur)
ON DELETE CASCADE;
ALTER TABLE projets
ADD CONSTRAINT fk2_projet FOREIGN KEY (id_entrepreneur_participation)
REFERENCES entrepreneurs (id_entrepreneur)
ON DELETE CASCADE;
ALTER TABLE entrepreneurs
ADD CONSTRAINT fk1_entrepreneur FOREIGN KEY (id_projet_propose)
REFERENCES projets (id_projet)
ON DELETE CASCADE;
ALTER TABLE sections
ADD CONSTRAINT fk1_section FOREIGN KEY (id_projet)
REFERENCES projets (id_projet)
ON DELETE CASCADE;
ALTER TABLE sections
ADD CONSTRAINT fk2_section FOREIGN KEY (id_administrateur)
REFERENCES administrateurs (id_administrateur)
ON DELETE CASCADE;
ALTER TABLE rendez-vous
ADD CONSTRAINT fk1_rdv FOREIGN KEY (id_entrepreneur)
REFERENCES entrepreneurs (id_entrepreneur)
ON
DELETE
CASCADE;
ALTER TABLE rendez-vous
ADD CONSTRAINT fk2_rdv FOREIGN KEY (id_administrateur)
REFERENCES administrateurs (id_administrateur)
ON
DELETE
CASCADE;
ALTER TABLE comptes-rendus
ADD CONSTRAINT fk1_compte_rendu FOREIGN KEY (id_rdv)
REFERENCES rendez_vous (id_rdv)
ON
DELETE
CASCADE;

View File

@ -7,7 +7,5 @@ import org.springframework.boot.test.context.SpringBootTest;
class MyinpulseApplicationTests {
@Test
void contextLoads() {
}
void contextLoads() {}
}

View File

@ -1,15 +1,14 @@
services:
postgres:
image: postgres:latest
env_file: .env
build:
context: postgres/
dockerfile: Dockerfile
container_name: MyINPulse-DB
#ports:
# - 5432:5432
ports:
- 5433:5432
volumes:
- ./postgres:/var/lib/postgresql/data
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
- ./postgres/data:/var/lib/postgresql/data
keycloak:
container_name: MyINPulse-keycloak

22
config/backdev.env Normal file
View File

@ -0,0 +1,22 @@
POSTGRES_DB=postgres_db
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres_db_user_password
KEYCLOAK_ADMIN=admin
KEYCLOAK_ADMIN_PASSWORD=admin
KEYCLOAK_HOSTNAME=localhost
KEYCLOAK_DB=keycloak_db
KEYCLOAK_USER=keycloak_db_user
KEYCLOAK_PASSWORD=keycloak_db_user_password
BACKEND_DB=backend_db
BACKEND_USER=backend_db_user
BACKEND_PASSWORD=backend_db_user_password
DATABASE_URL=localhost:5433
VITE_KEYCLOAK_URL=http://localhost:7080
VITE_KEYCLOAK_CLIENT_ID=myinpulse
VITE_KEYCLOAK_REALM=test
VITE_APP_URL=http://localhost:8080
VITE_BACKEND_URL=http://localhost:8081/

View File

@ -1,5 +0,0 @@
VITE_KEYCLOAK_URL=http://localhost:7080
VITE_KEYCLOAK_CLIENT_ID=myinpulse
VITE_KEYCLOAK_REALM=test
VITE_APP_URL=http://localhost:8080
VITE_BACKEND_URL=http://localhost:8081/

View File

@ -1,6 +0,0 @@
POSTGRES_DB=keycloak_db
POSTGRES_USER=keycloak_db_user
POSTGRES_PASSWORD=keycloak_db_user_password
KEYCLOAK_ADMIN=admin
KEYCLOAK_ADMIN_PASSWORD=admin
KEYCLOAK_HOSTNAME=localhost

View File

@ -0,0 +1,52 @@
services:
postgres:
env_file: .env
build:
context: postgres/
dockerfile: Dockerfile
container_name: MyINPulse-DB
ports:
- 5433:5432
volumes:
- ./postgres/data:/var/lib/postgresql/data
keycloak:
container_name: MyINPulse-keycloak
build:
context: ./keycloak
dockerfile: Dockerfile
args:
KC_DB: postgres
KC_DB_URL: jdbc:postgresql://postgres/${POSTGRES_DB}
KC_DB_USERNAME: ${POSTGRES_USER}
KC_DB_PASSWORD: ${POSTGRES_PASSWORD}
environment:
KC_HOSTNAME_PORT: 7080
KC_HOSTNAME_STRICT_BACKCHANNEL: "true"
KC_BOOTSTRAP_ADMIN_USERNAME: ${KEYCLOAK_ADMIN}
KC_BOOTSTRAP_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD}
KC_LOG_LEVEL: info
command: ["start-dev", "--http-port", "7080", "--https-port", "7443", "--hostname", "${KEYCLOAK_HOSTNAME}"]
ports:
- "7080:7080"
- "7443:7443"
depends_on:
- postgres
#front:
# build:
# context: ./front/
# dockerfile: Dockerfile
# container_name: MyINPulse-front
# ports:
# - "8080:80"
#back:
# build:
# context: ./MyINPulse-back/
# dockerfile: Dockerfile
# container_name: MyINPulse-back
# ports:
# - "8081:8080"

22
config/dev.env Normal file
View File

@ -0,0 +1,22 @@
POSTGRES_DB=postgres_db
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres_db_user_password
KEYCLOAK_ADMIN=admin
KEYCLOAK_ADMIN_PASSWORD=admin
KEYCLOAK_HOSTNAME=localhost
KEYCLOAK_DB=keycloak_db
KEYCLOAK_USER=keycloak_db_user
KEYCLOAK_PASSWORD=keycloak_db_user_password
BACKEND_DB=backend_db
BACKEND_USER=backend_db_user
BACKEND_PASSWORD=backend_db_user_password
DATABASE_URL=localhost:5433
VITE_KEYCLOAK_URL=http://localhost:7080
VITE_KEYCLOAK_CLIENT_ID=myinpulse-dev
VITE_KEYCLOAK_REALM=test
VITE_APP_URL=http://localhost:5173
VITE_BACKEND_URL=http://localhost:8081/

View File

@ -1,15 +1,15 @@
services:
postgres:
image: postgres:latest
env_file: .env
build:
context: postgres/
dockerfile: Dockerfile
container_name: MyINPulse-DB
#ports:
# - 5432:5432
volumes:
- ./postgres:/var/lib/postgresql/data
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
- ./postgres/data:/var/lib/postgresql/data
keycloak:
container_name: MyINPulse-keycloak

22
config/frontdev.env Normal file
View File

@ -0,0 +1,22 @@
POSTGRES_DB=postgres_db
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres_db_user_password
KEYCLOAK_ADMIN=admin
KEYCLOAK_ADMIN_PASSWORD=admin
KEYCLOAK_HOSTNAME=localhost
KEYCLOAK_DB=keycloak_db
KEYCLOAK_USER=keycloak_db_user
KEYCLOAK_PASSWORD=keycloak_db_user_password
BACKEND_DB=backend_db
BACKEND_USER=backend_db_user
BACKEND_PASSWORD=backend_db_user_password
DATABASE_URL=MyINPulse-DB
VITE_KEYCLOAK_URL=http://localhost:7080
VITE_KEYCLOAK_CLIENT_ID=myinpulse-dev
VITE_KEYCLOAK_REALM=test
VITE_APP_URL=http://localhost:5173
VITE_BACKEND_URL=http://localhost:8081/

View File

@ -1,5 +0,0 @@
VITE_KEYCLOAK_URL=http://localhost:7080
VITE_KEYCLOAK_CLIENT_ID=myinpulse-dev
VITE_KEYCLOAK_REALM=test
VITE_APP_URL=http://localhost:5173
VITE_BACKEND_URL=http://localhost:8081/

View File

@ -1,6 +0,0 @@
POSTGRES_DB=keycloak_db
POSTGRES_USER=keycloak_db_user
POSTGRES_PASSWORD=keycloak_db_user_password
KEYCLOAK_ADMIN=admin
KEYCLOAK_ADMIN_PASSWORD=admin
KEYCLOAK_HOSTNAME=localhost

View File

@ -1,11 +1,14 @@
services:
postgres:
image: postgres:latest
env_file: .env
build:
context: postgres/
dockerfile: Dockerfile
container_name: MyINPulse-DB
#ports:
# - 5432:5432
volumes:
- ./postgres:/var/lib/postgresql/data
- ./postgres/data:/var/lib/postgresql/data
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
@ -27,10 +30,10 @@ services:
KC_BOOTSTRAP_ADMIN_USERNAME: ${KEYCLOAK_ADMIN}
KC_BOOTSTRAP_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD}
KC_LOG_LEVEL: info
command: ["start-dev", "--http-port", "7080", "--https-port", "7443", "--hostname", "${KEYCLOAK_HOSTNAME}"]
ports:
- "7080:7080"
- "7443:7443"
command: ["start-dev", "--http-port", "7080", "--https-port", "7443", "--hostname", "${KEYCLOAK_HOSTNAME}"] # TODO: remove start-dev
#ports:
# - "7080:7080"
# - "7443:7443"
depends_on:
- postgres
@ -47,6 +50,6 @@ services:
context: ./MyINPulse-back/
dockerfile: Dockerfile
container_name: MyINPulse-back
ports:
- "8081:8080"
#ports:
# - "8081:8080"

22
config/prod.env Normal file
View File

@ -0,0 +1,22 @@
POSTGRES_DB=postgres_db
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres_db_user_password
KEYCLOAK_ADMIN=admin
KEYCLOAK_ADMIN_PASSWORD=admin
KEYCLOAK_HOSTNAME=0549cd63f912d5dc9b31278d6f.eirb.fr
KEYCLOAK_DB=keycloak_db
KEYCLOAK_USER=keycloak_db_user
KEYCLOAK_PASSWORD=keycloak_db_user_password
BACKEND_DB=backend_db
BACKEND_USER=backend_db_user
BACKEND_PASSWORD=backend_db_user_password
DATABASE_URL=MyINPulse-DB
VITE_KEYCLOAK_URL=https://0549cd63f912d5dc9b31278d6f.eirb.fr
VITE_KEYCLOAK_CLIENT_ID=myinpulse-eirb
VITE_KEYCLOAK_REALM=test
VITE_APP_URL=https://0549cd63f912d5dc9b31278d6f.piair.dev
VITE_BACKEND_URL=http://TODO/

View File

@ -1,5 +0,0 @@
VITE_KEYCLOAK_URL=https://0549cd63f912d5dc9b31278d6f.eirb.fr
VITE_KEYCLOAK_CLIENT_ID=myinpulse-eirb
VITE_KEYCLOAK_REALM=test
VITE_APP_URL=https://0549cd63f912d5dc9b31278d6f.piair.dev
VITE_BACKEND_URL=http://TODO/

View File

@ -1,6 +0,0 @@
POSTGRES_DB=keycloak_db
POSTGRES_USER=keycloak_db_user
POSTGRES_PASSWORD=keycloak_db_user_password
KEYCLOAK_ADMIN=admin
KEYCLOAK_ADMIN_PASSWORD=admin
KEYCLOAK_HOSTNAME=0549cd63f912d5dc9b31278d6f.eirb.fr

View File

@ -36,4 +36,4 @@ playwright-report/
# Custom
.installed
package-lock.json
./package-lock.json

View File

@ -0,0 +1,7 @@
{
"useTabs":false,
"semi":true,
"trailingComma":"es5",
"arrowParens":"always",
"tabWidth":4
}

View File

@ -0,0 +1,29 @@
import eslint from "@eslint/js";
import eslintConfigPrettier from "eslint-config-prettier";
import eslintPluginVue from "eslint-plugin-vue";
import globals from "globals";
import typescriptEslint from "typescript-eslint";
export default typescriptEslint.config(
{ ignores: ["*.d.ts", "**/coverage", "**/dist"] },
{
extends: [
eslint.configs.recommended,
...typescriptEslint.configs.recommended,
...eslintPluginVue.configs["flat/recommended"],
],
files: ["**/*.{ts,vue}"],
languageOptions: {
ecmaVersion: "latest",
sourceType: "module",
globals: globals.browser,
parserOptions: {
parser: typescriptEslint.parser,
},
},
rules: {
// your rules
},
},
eslintConfigPrettier
);

File diff suppressed because it is too large Load Diff

View File

@ -26,8 +26,15 @@
"@types/node": "^22.10.7",
"@vitejs/plugin-vue": "^5.2.1",
"@vue/tsconfig": "^0.7.0",
"eslint": "^9.20.0",
"eslint-config-prettier": "^10.0.1",
"eslint-plugin-vue": "^9.32.0",
"globals": "^15.14.0",
"jiti": "^2.4.2",
"npm-run-all2": "^7.0.2",
"prettier": "3.5.0",
"typescript": "~5.7.3",
"typescript-eslint": "^8.23.0",
"vite": "^6.0.11",
"vite-plugin-vue-devtools": "^7.7.0",
"vue-tsc": "^2.2.0"

View File

@ -1,44 +1,40 @@
<script setup lang="ts">
import { RouterLink, RouterView } from 'vue-router'
import { RouterView } from "vue-router";
import ErrorWrapper from "@/views/errorWrapper.vue";
import ProjectComponent from "@/components/ProjectComponent.vue";
</script>
<template>
<Header />
<HeaderComponent />
<error-wrapper></error-wrapper>
<div id="main">
<ProjectComp
<ProjectComponent
v-for="(project, index) in projects"
:key="index"
:projectName="project.name"
:project-name="project.name"
/>
</div>
<RouterView />
</template>
<style scoped>
</style>
<script lang="ts">
import Header from "@/components/Header.vue";
import ProjectComp from "@/components/Project-comp.vue";
import HeaderComponent from "@/components/HeaderComponent.vue";
export default {
name: 'App',
name: "App",
components: {
Header,
ProjectComp,
HeaderComponent,
},
data() {
return {
projects: [
{
name: 'Projet Alpha',
name: "Projet Alpha",
//link: './project-alpha.html',
//members: ['Alice', 'Bob', 'Charlie'],
},
{
name: 'Projet Beta',
name: "Projet Beta",
//link: './project-beta.html',
//members: ['David', 'Eve', 'Frank'],
},
@ -46,5 +42,6 @@ export default {
};
},
};
</script>
<style scoped></style>

View File

@ -6,7 +6,7 @@
<script lang="ts">
export default {
name: 'Header',
name: "HeaderComponent",
};
</script>

View File

@ -3,16 +3,19 @@
<div class="project-header">
<h2>{{ projectName }}</h2>
</div>
</div>
</template>
<script lang="ts">
import type { PropType } from "vue";
export default {
name: 'Project',
name: "ProjectComponent",
props: {
projectName: String,
}
}
projectName: {
type: Object as PropType<string>,
required: true,
},
},
};
</script>

View File

@ -1,12 +1,38 @@
<script setup lang="ts">
const props = defineProps(['data']);
import { color } from "@/services/popupDisplayer.ts";
import type { PropType } from "vue";
defineProps({
errorMessage: {
type: Object as PropType<string>,
required: true,
},
errorColor: {
type: Object as PropType<color>,
default: color.Red,
},
});
</script>
<template>
<div :class='["red", "yellow", "blue", "green"][data.type]' class="error-modal">
<p>{{["Erreur :(", "Warning :|", "Info :)", "Succes ;)"][data.type]}}</p>
<p>{{data.errorMessage}}</p>
<div class="loading" :class='["red-loader", "yellow-loader", "blue-loader", "green-loader"][data.type]'></div>
<div
:class="['red', 'yellow', 'blue', 'green'][errorColor]"
class="error-modal"
>
<p>
{{
["Erreur :(", "Warning :|", "Info :)", "Succes ;)"][errorColor]
}}
</p>
<p>{{ errorMessage }}</p>
<div
class="loading"
:class="
['red-loader', 'yellow-loader', 'blue-loader', 'green-loader'][
errorColor
]
"
></div>
</div>
</template>
@ -25,14 +51,16 @@ const props = defineProps(['data']);
background-color: #ee6055;
color: white;
}
.red-loader {
background-color: #fa8383;
}
.yellow {
background-color: #FF9D23;
background-color: #ff9d23;
color: white;
}
.yellow-loader {
background-color: #ffbf81;
}
@ -41,6 +69,7 @@ const props = defineProps(['data']);
background-color: #809bce;
color: white;
}
.blue-loader {
background-color: #95b8d1;
}
@ -49,6 +78,7 @@ const props = defineProps(['data']);
background-color: green;
color: white;
}
.green-loader {
background-color: darkgreen;
}

View File

@ -0,0 +1,18 @@
<script setup lang="ts">
import { addNewMessage } from "@/services/popupDisplayer.ts";
</script>
<template>
<button
@click="
addNewMessage(
'new error from another view',
Math.floor(Math.random() * 4)
)
"
>
Add an error
</button>
</template>
<style scoped></style>

View File

@ -1,36 +1,31 @@
import { createApp } from 'vue'
import App from './App.vue'
import router from './router/router.ts'
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router/router.ts";
import { createPinia } from "pinia";
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate';
import AuthStorePlugin from './plugins/authStore';
import keycloakService from './services/keycloak';
import {useAuthStore} from "@/stores/authStore.ts";
let store: any;
import piniaPluginPersistedstate from "pinia-plugin-persistedstate";
import keycloakService from "./services/keycloak";
import { type AuthStore, useAuthStore } from "@/stores/authStore.ts";
let store: AuthStore;
keycloakService.CallInit(() => {
try {
const app = createApp(App)
const app = createApp(App);
// Setup pinia store, allowing user to keep logged in status after refresh
const pinia = createPinia();
pinia.use(piniaPluginPersistedstate);
app.use(pinia);
app.use(AuthStorePlugin, { pinia });
store = useAuthStore();
app.use(router)
keycloakService.CallInitStore(store);
app.use(router);
app.mount('#app');
app.mount("#app");
} catch (e) {
console.error("Error while initiating Keycloak.")
console.error(e)
createApp(App).mount('#app');
console.error("Error while initiating Keycloak.");
console.error(e);
createApp(App).mount("#app");
}
})
});
export { store };

View File

@ -1,14 +0,0 @@
// file: src/plugins/authStore.js
import { useAuthStore } from "@/stores/authStore.ts";
import keycloakService from '@/services/keycloak';
// Setup auth store as a plugin so it can be accessed globally in our FE
const authStorePlugin = {
install(app: any, option: any) {
const store = useAuthStore(option.pinia);
app.config.globalProperties.$store = store;
keycloakService.CallInitStore(store);
}
}
export default authStorePlugin;

View File

@ -1,17 +1,17 @@
import { createRouter, createWebHistory } from 'vue-router'
import { createRouter, createWebHistory } from "vue-router";
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/test',
name: 'test',
path: "/test",
name: "test",
// route level code-splitting
// this generates a separate chunk (About.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import('../views/test.vue'),
component: () => import("../views/testComponent.vue"),
},
],
})
});
export default router
export default router;

View File

@ -1,31 +1,37 @@
import axios from "axios";
import axios, { type AxiosError, type AxiosResponse } from "axios";
import { store } from "@/main.ts";
import { addNewMessage, color } from "@/services/popupDisplayer.ts";
const axiosInstance = axios.create({
baseURL: import.meta.env.VITE_BACKEND_URL,
headers: {
'Content-Type': 'application/json',
"Content-Type": "application/json",
},
});
axiosInstance.interceptors.response.use(
response => response, // Directly return successful responses.
async error => {
(response) => response, // Directly return successful responses.
async (error) => {
const originalRequest = error.config;
if (error.response.status === 401 && !originalRequest._retry && store.authenticated) {
if (
((error.response && error.response.status === 401) ||
error.code == "ERR_NETWORK") &&
!originalRequest._retry &&
store.authenticated
) {
originalRequest._retry = true; // Mark the request as retried to avoid infinite loops.
try {
await store.refreshUserToken();
// Update the authorization header with the new access token.
axiosInstance.defaults.headers.common['Authorization'] = `Bearer ${store.user.token}`;
axiosInstance.defaults.headers.common["Authorization"] =
`Bearer ${store.user.token}`;
return axiosInstance(originalRequest); // Retry the original request with the new access token.
} catch (refreshError) {
// Handle refresh token errors by clearing stored tokens and redirecting to the login page.
console.error('Token refresh failed:', refreshError);
localStorage.removeItem('accessToken');
localStorage.removeItem('refreshToken');
window.location.href = '/login';
console.error("Token refresh failed:", refreshError);
localStorage.removeItem("accessToken");
localStorage.removeItem("refreshToken");
window.location.href = "/login";
return Promise.reject(refreshError);
}
}
@ -34,23 +40,29 @@ axiosInstance.interceptors.response.use(
);
// TODO: spawn a error modal
function defaultApiErrorHandler(err: string){
addNewMessage(err, color.Red);
function defaultApiErrorHandler(err: AxiosError) {
addNewMessage(err.message, color.Red);
}
function defaultApiSuccessHandler(response: any){
addNewMessage(response.data, color.green)
}
function callApi(endpoint: string, onSuccessHandler?: any, onErrorHandler?: any): void {
axiosInstance.get(endpoint).then(
onSuccessHandler == null ? defaultApiSuccessHandler : onSuccessHandler
).catch(
(err) => {
onErrorHandler == null ? defaultApiErrorHandler(err): onErrorHandler(err);
throw err;
function defaultApiSuccessHandler(response: AxiosResponse) {
addNewMessage(response.data, color.Green);
}
function callApi(
endpoint: string,
onSuccessHandler?: (response: AxiosResponse) => void,
onErrorHandler?: (error: AxiosError) => void
): void {
axiosInstance
.get(endpoint)
.then(
onSuccessHandler == null
? defaultApiSuccessHandler
: onSuccessHandler
)
.catch(
onErrorHandler == null ? defaultApiErrorHandler : onErrorHandler
);
}
export {callApi}
export { callApi };

View File

@ -1,11 +1,11 @@
import Keycloak from 'keycloak-js';
import Keycloak from "keycloak-js";
import type { AuthStore } from "@/stores/authStore.ts";
const options = {
url: import.meta.env.VITE_KEYCLOAK_URL,
clientId: import.meta.env.VITE_KEYCLOAK_CLIENT_ID,
realm: import.meta.env.VITE_KEYCLOAK_REALM
}
realm: import.meta.env.VITE_KEYCLOAK_REALM,
};
const keycloak = new Keycloak(options);
let authenticated: boolean | undefined;
@ -13,21 +13,19 @@ let store = null;
async function login() {
try {
await keycloak.login() // https://www.keycloak.org/securing-apps/javascript-adapter#:~:text=when%20initialization%20completes.-,login(options),-Redirects%20to%20login
await keycloak.login(); // https://www.keycloak.org/securing-apps/javascript-adapter#:~:text=when%20initialization%20completes.-,login(options),-Redirects%20to%20login
return keycloak;
} catch (error) {
console.log(error)
console.log(error);
}
}
async function signup() {
try {
await keycloak.login(
{action: "register"}
) // https://www.keycloak.org/securing-apps/javascript-adapter#:~:text=when%20initialization%20completes.-,login(options),-Redirects%20to%20login
await keycloak.login({ action: "register" }); // https://www.keycloak.org/securing-apps/javascript-adapter#:~:text=when%20initialization%20completes.-,login(options),-Redirects%20to%20login
return keycloak;
} catch (error) {
console.log(error)
console.log(error);
}
}
@ -42,31 +40,33 @@ async function init(onInitCallback: () => void) {
onLoad: "check-sso",
silentCheckSsoRedirectUri: `${location.origin}/silent-check-sso.htm`,
responseMode: "query",
})
onInitCallback()
});
onInitCallback();
} catch (error) {
console.error("Keycloak init failed")
console.error(error)
console.error("Keycloak init failed");
console.error(error);
}
}
};
/**
* Initializes store with Keycloak user data
*
*/
async function initStore(storeInstance: any) {
async function initStore(storeInstance: AuthStore) {
try {
store = storeInstance
console.log(keycloak)
await store.initOauth(keycloak)
store = storeInstance;
console.log(keycloak);
await store.initOauth(keycloak);
// Show alert if user is not authenticated
if (!authenticated) { console.warn("not authenticated") }
} catch (error) {
console.error("Keycloak init failed")
console.error(error)
if (!authenticated) {
console.warn("not authenticated");
}
} catch (error) {
console.error("Keycloak init failed");
console.error(error);
}
}
};
/**
* Logout user
@ -83,7 +83,7 @@ async function refreshToken() {
await keycloak.updateToken(480);
return keycloak;
} catch (error) {
console.error('Failed to refresh token');
console.error("Failed to refresh token");
console.error(error);
}
}

View File

@ -1,5 +1,24 @@
import {ref} from "vue";
enum color {Red, Yellow, Blue, green}
import { ref, type Ref } from "vue";
enum color {
Red,
Yellow,
Blue,
Green,
}
type ErrorMessageContent = {
message: string;
color: color;
id: number;
timeout: number;
};
let id: number = 0;
const getId = () => {
id = id + 1;
return id;
};
function addNewMessage(errorMessage: string, type?: color, timeout?: number) {
if (timeout == null) {
@ -9,11 +28,16 @@ function addNewMessage(errorMessage: string, type?: color, timeout?: number){
type = color.Red;
}
const data = {errorMessage: errorMessage, timeout: timeout, type: type, uid: Math.random()*100000};
errorList.value.push(data)
setTimeout(() => errorList.value.slice(0, 1), timeout)
const data: ErrorMessageContent = {
message: errorMessage,
timeout: timeout,
color: type,
id: getId(),
};
errorList.value.push(data);
setTimeout(() => errorList.value.slice(0, 1), timeout);
}
const errorList: any= ref([])
const errorList: Ref<ErrorMessageContent[]> = ref([]);
export {addNewMessage, errorList, color}
export { addNewMessage, errorList, color, type ErrorMessageContent };

View File

@ -1,27 +1,34 @@
import { defineStore } from "pinia";
import keycloakService from '@/services/keycloak';
import keycloakService from "@/services/keycloak";
import type Keycloak from "keycloak-js";
export const useAuthStore = defineStore("storeAuth", {
const useAuthStore = defineStore("storeAuth", {
state: () => {
return {
testv: true,
authenticated: false,
user: {
token: "",
refreshToken: "",
username: "",
},
}
};
},
persist: true,
getters: {},
actions: {
// Initialize Keycloak OAuth
async initOauth(keycloak: Keycloak, clearData = true) {
if(clearData) { await this.clearUserData(); }
if (clearData) {
await this.clearUserData();
}
this.authenticated = !!keycloak.authenticated; // the !! removes undefined
if (this.authenticated && keycloak.token && keycloak.idTokenParsed && keycloak.refreshToken){
if (
this.authenticated &&
keycloak.token &&
keycloak.idTokenParsed &&
keycloak.refreshToken
) {
this.user.username = keycloak.idTokenParsed.given_name;
this.user.token = keycloak.token;
this.user.refreshToken = keycloak.refreshToken;
@ -30,25 +37,25 @@ export const useAuthStore = defineStore("storeAuth", {
async login() {
try {
const keycloak = await keycloakService.callLogin();
if (keycloak)
await this.initOauth(keycloak);
if (keycloak) await this.initOauth(keycloak);
} catch (error) {
console.log(error)
console.log(error);
}
},
async signup() {
try {
const keycloak = await keycloakService.callSignup();
if (keycloak)
await this.initOauth(keycloak);
if (keycloak) await this.initOauth(keycloak);
} catch (error) {
console.log(error)
console.log(error);
}
},
// Logout user
async logout() {
try {
await keycloakService.CallLogout(import.meta.env.VITE_APP_URL + "/test");
await keycloakService.CallLogout(
import.meta.env.VITE_APP_URL + "/test"
);
await this.clearUserData();
} catch (error) {
console.error(error);
@ -58,15 +65,11 @@ export const useAuthStore = defineStore("storeAuth", {
async refreshUserToken() {
try {
const keycloak = await keycloakService.CallTokenRefresh();
if (keycloak)
await this.initOauth(keycloak, false);
if (keycloak) await this.initOauth(keycloak, false);
} catch (error) {
console.error(error);
}
},
test() {
this.testv = !this.testv;
},
// Clear user's store data
clearUserData() {
this.authenticated = false;
@ -75,6 +78,10 @@ export const useAuthStore = defineStore("storeAuth", {
refreshToken: "",
username: "",
};
}
}
},
},
});
type AuthStore = ReturnType<typeof useAuthStore>;
export { useAuthStore, type AuthStore };

View File

@ -1,15 +0,0 @@
<template>
<div class="about">
<h1>This is an about page</h1>
</div>
</template>
<style>
@media (min-width: 1024px) {
.about {
min-height: 100vh;
display: flex;
align-items: center;
}
}
</style>

View File

@ -1,12 +1,16 @@
<script setup lang="ts">
import { errorList } from "@/services/popupDisplayer.ts";
import ErrorModal from "@/components/errorModal.vue";
</script>
<template>
<div class="error-wrapper">
<error-modal v-for="elm in errorList" :data=elm></error-modal>
<error-modal
v-for="elm in errorList"
:key="elm.id"
:error-message="elm.message"
:error-color="elm.color"
/>
</div>
</template>
@ -17,6 +21,5 @@ import ErrorModal from "@/components/errorModal.vue";
//background-color: blue;
height: 100%;
width: 30%;
}
</style>

View File

@ -1,75 +0,0 @@
<script setup lang="ts">
import {store} from "../main.ts";
import {callApi} from "@/services/api.ts";
import ErrorModal from "@/components/errorModal.vue";
import {errorList} from "@/services/popupDisplayer.ts";
import TempModal from "@/components/temp-modal.vue";
import ErrorWrapper from "@/App.vue";
function addResToTable(id: any){
return (req: any) => {
console.log(req)
}
}
</script>
<template>
<h1>Test page</h1>
<table class="test" style="width:100%">
<tbody>
<tr>
<td>Is Currently Authenticated ? </td>
<td>{{store.authenticated}}</td>
<td><button @click="store.login">Login</button></td>
<td><button @click="store.logout">Logout</button></td>
<td><button @click="store.signup">Signup</button></td>
</tr>
<tr>
<td>current token</td>
<td>{{store.user.token}}</td>
<td><button @click="store.refreshUserToken">Refresh</button></td>
</tr>
<tr>
<td>Current refresh token</td>
<td>{{store.user.refreshToken}}</td>
</tr>
<tr>
<td>Entrepreneur API call</td>
<td><button @click="callApi('random')">call</button></td>
<td>res</td>
<td></td>
</tr>
<tr>
<td>Admin API call</td>
<td><button @click="callApi('random2')">call</button></td>
<td>res</td>
<td></td>
</tr>
<tr>
<td>Unauth API call</td>
<td><button @click="callApi('random3')">call</button></td>
<td>res</td>
<td id="3"></td>
</tr>
</tbody>
</table>
<temp-modal></temp-modal>
</template>
<style scoped>
table {
width: 100px;
table-layout: fixed
}
tr {
width: 100%;
height: 100%;
}
td {
border: solid 1px black;
width: 20%;
height: 100%;
overflow: hidden;
}
</style>

View File

@ -0,0 +1,90 @@
<script lang="ts" setup>
import { store } from "../main.ts";
import { callApi } from "@/services/api.ts";
import { ref } from "vue";
const CustomRequest = ref("");
</script>
<template>
<h1>Test page</h1>
<table class="test" style="width: 100%">
<tbody>
<tr>
<td>Is Currently Authenticated ?</td>
<td>{{ store.authenticated }}</td>
<td>
<button @click="store.login">Login</button>
</td>
<td>
<button @click="store.logout">Logout</button>
</td>
<td>
<button @click="store.signup">Signup</button>
</td>
</tr>
<tr>
<td>current token</td>
<td>{{ store.user.token }}</td>
<td>
<button @click="store.refreshUserToken">Refresh</button>
</td>
</tr>
<tr>
<td>Current refresh token</td>
<td>{{ store.user.refreshToken }}</td>
</tr>
<tr>
<td>Entrepreneur API call</td>
<td>
<button @click="callApi('random')">call</button>
</td>
<td>res</td>
<td></td>
</tr>
<tr>
<td>Admin API call</td>
<td>
<button @click="callApi('random2')">call</button>
</td>
<td>res</td>
<td></td>
</tr>
<tr>
<td>Unauth API call</td>
<td>
<button @click="callApi('random3')">call</button>
</td>
<td>res</td>
<td id="3"></td>
</tr>
<tr>
<td>
<input v-model="CustomRequest" placeholder="edit me" />
</td>
<td>
<button @click="callApi(CustomRequest)">call</button>
</td>
</tr>
</tbody>
</table>
</template>
<style scoped>
table {
width: 100px;
table-layout: fixed;
}
tr {
width: 100%;
height: 100%;
}
td {
border: solid 1px black;
width: 20%;
height: 100%;
overflow: hidden;
}
</style>

5
postgres/Dockerfile Normal file
View File

@ -0,0 +1,5 @@
FROM postgres:latest
# Custom initialization scripts
COPY ./create_user.sh /docker-entrypoint-initdb.d/10-create_user.sh
COPY ./create_db.sh /docker-entrypoint-initdb.d/20-create_db.sh

17
postgres/create_db.sh Normal file
View File

@ -0,0 +1,17 @@
#!/bin/bash
set -e
POSTGRES="psql --username ${POSTGRES_USER}"
echo "Creating database: ${DB_NAME}"
$POSTGRES <<EOSQL
CREATE DATABASE ${BACKEND_DB} OWNER ${BACKEND_USER};
EOSQL
echo "Creating database: ${DB_NAME}"
$POSTGRES <<EOSQL
CREATE DATABASE ${KEYCLOAK_DB} OWNER ${KEYCLOAK_USER};
EOSQL

16
postgres/create_user.sh Normal file
View File

@ -0,0 +1,16 @@
#!/bin/bash
set -e
POSTGRES="psql --username ${POSTGRES_USER}"
echo "Creating database role: [${BACKEND_USER}]"
$POSTGRES <<-EOSQL
CREATE USER ${BACKEND_USER} WITH CREATEDB PASSWORD '${BACKEND_PASSWORD}';
EOSQL
echo "Creating database role: ${KEYCLOAK_USER}"
$POSTGRES <<-EOSQL
CREATE USER ${KEYCLOAK_USER} WITH CREATEDB PASSWORD '${KEYCLOAK_PASSWORD}';
EOSQL