This commit is contained in:
parent
a859871265
commit
645a10477d
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"useTabs":false,
|
||||||
|
"semi":true,
|
||||||
|
"trailingComma":"es5",
|
||||||
|
"arrowParens":"always",
|
||||||
|
"tabWidth":4
|
||||||
|
}
|
@ -1,29 +1,29 @@
|
|||||||
import eslint from '@eslint/js';
|
import eslint from "@eslint/js";
|
||||||
import eslintConfigPrettier from 'eslint-config-prettier';
|
import eslintConfigPrettier from "eslint-config-prettier";
|
||||||
import eslintPluginVue from 'eslint-plugin-vue';
|
import eslintPluginVue from "eslint-plugin-vue";
|
||||||
import globals from 'globals';
|
import globals from "globals";
|
||||||
import typescriptEslint from 'typescript-eslint';
|
import typescriptEslint from "typescript-eslint";
|
||||||
|
|
||||||
export default typescriptEslint.config(
|
export default typescriptEslint.config(
|
||||||
{ ignores: ['*.d.ts', '**/coverage', '**/dist'] },
|
{ ignores: ["*.d.ts", "**/coverage", "**/dist"] },
|
||||||
{
|
{
|
||||||
extends: [
|
extends: [
|
||||||
eslint.configs.recommended,
|
eslint.configs.recommended,
|
||||||
...typescriptEslint.configs.recommended,
|
...typescriptEslint.configs.recommended,
|
||||||
...eslintPluginVue.configs['flat/recommended'],
|
...eslintPluginVue.configs["flat/recommended"],
|
||||||
],
|
],
|
||||||
files: ['**/*.{ts,vue}'],
|
files: ["**/*.{ts,vue}"],
|
||||||
languageOptions: {
|
languageOptions: {
|
||||||
ecmaVersion: 'latest',
|
ecmaVersion: "latest",
|
||||||
sourceType: 'module',
|
sourceType: "module",
|
||||||
globals: globals.browser,
|
globals: globals.browser,
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
parser: typescriptEslint.parser,
|
parser: typescriptEslint.parser,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
// your rules
|
// your rules
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
eslintConfigPrettier
|
eslintConfigPrettier
|
||||||
);
|
);
|
||||||
|
1397
front/MyINPulse-front/package-lock.json
generated
1397
front/MyINPulse-front/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -32,6 +32,7 @@
|
|||||||
"globals": "^15.14.0",
|
"globals": "^15.14.0",
|
||||||
"jiti": "^2.4.2",
|
"jiti": "^2.4.2",
|
||||||
"npm-run-all2": "^7.0.2",
|
"npm-run-all2": "^7.0.2",
|
||||||
|
"prettier": "3.5.0",
|
||||||
"typescript": "~5.7.3",
|
"typescript": "~5.7.3",
|
||||||
"typescript-eslint": "^8.23.0",
|
"typescript-eslint": "^8.23.0",
|
||||||
"vite": "^6.0.11",
|
"vite": "^6.0.11",
|
||||||
|
@ -1,49 +1,47 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { RouterView } from 'vue-router'
|
import { RouterView } from "vue-router";
|
||||||
import ErrorWrapper from "@/views/errorWrapper.vue";
|
import ErrorWrapper from "@/views/errorWrapper.vue";
|
||||||
import ProjectComponent from "@/components/ProjectComponent.vue";
|
import ProjectComponent from "@/components/ProjectComponent.vue";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<HeaderComponent />
|
<HeaderComponent />
|
||||||
<error-wrapper></error-wrapper>
|
<error-wrapper></error-wrapper>
|
||||||
<div id="main">
|
<div id="main">
|
||||||
<ProjectComponent
|
<ProjectComponent
|
||||||
v-for="(project, index) in projects"
|
v-for="(project, index) in projects"
|
||||||
:key="index"
|
:key="index"
|
||||||
:project-name="project.name"
|
:project-name="project.name"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<RouterView />
|
<RouterView />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import HeaderComponent from "@/components/HeaderComponent.vue";
|
import HeaderComponent from "@/components/HeaderComponent.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'App',
|
name: "App",
|
||||||
components: {
|
components: {
|
||||||
HeaderComponent,
|
HeaderComponent,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
projects: [
|
projects: [
|
||||||
{
|
{
|
||||||
name: 'Projet Alpha',
|
name: "Projet Alpha",
|
||||||
//link: './project-alpha.html',
|
//link: './project-alpha.html',
|
||||||
//members: ['Alice', 'Bob', 'Charlie'],
|
//members: ['Alice', 'Bob', 'Charlie'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Projet Beta',
|
name: "Projet Beta",
|
||||||
//link: './project-beta.html',
|
//link: './project-beta.html',
|
||||||
//members: ['David', 'Eve', 'Frank'],
|
//members: ['David', 'Eve', 'Frank'],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped></style>
|
||||||
</style>
|
|
||||||
|
@ -1,26 +1,26 @@
|
|||||||
<template>
|
<template>
|
||||||
<header>
|
<header>
|
||||||
<img src="./icons/logo inpulse.png" alt="INPulse" />
|
<img src="./icons/logo inpulse.png" alt="INPulse" />
|
||||||
</header>
|
</header>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
export default {
|
|
||||||
name: 'HeaderComponent',
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
header img {
|
|
||||||
width: 100px;
|
|
||||||
}
|
|
||||||
|
|
||||||
header{
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: "HeaderComponent",
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
header img {
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 10px 20px;
|
padding: 10px 20px;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border-bottom: 2px solid #ddd;
|
border-bottom: 2px solid #ddd;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -3,21 +3,19 @@
|
|||||||
<div class="project-header">
|
<div class="project-header">
|
||||||
<h2>{{ projectName }}</h2>
|
<h2>{{ projectName }}</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type {PropType} from "vue";
|
import type { PropType } from "vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ProjectComponent',
|
name: "ProjectComponent",
|
||||||
props: {
|
props: {
|
||||||
projectName: {
|
projectName: {
|
||||||
type: Object as PropType<string>,
|
type: Object as PropType<string>,
|
||||||
required: true
|
required: true,
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,29 +1,43 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {color} from "@/services/popupDisplayer.ts";
|
import { color } from "@/services/popupDisplayer.ts";
|
||||||
import type {PropType} from "vue";
|
import type { PropType } from "vue";
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
errorMessage: {
|
errorMessage: {
|
||||||
type: Object as PropType<string>,
|
type: Object as PropType<string>,
|
||||||
required: true
|
required: true,
|
||||||
},
|
},
|
||||||
errorColor: {
|
errorColor: {
|
||||||
type: Object as PropType<color>,
|
type: Object as PropType<color>,
|
||||||
default: color.Red
|
default: color.Red,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class='["red", "yellow", "blue", "green"][errorColor]' class="error-modal">
|
<div
|
||||||
<p>{{["Erreur :(", "Warning :|", "Info :)", "Succes ;)"][errorColor]}}</p>
|
:class="['red', 'yellow', 'blue', 'green'][errorColor]"
|
||||||
<p>{{errorMessage}}</p>
|
class="error-modal"
|
||||||
<div class="loading" :class='["red-loader", "yellow-loader", "blue-loader", "green-loader"][errorColor]'></div>
|
>
|
||||||
</div>
|
<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>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.error-modal{
|
.error-modal {
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
border-radius: 1em;
|
border-radius: 1em;
|
||||||
@ -31,41 +45,45 @@ defineProps({
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
animation: disappear 5s linear forwards;
|
animation: disappear 5s linear forwards;
|
||||||
}
|
}
|
||||||
|
|
||||||
.red{
|
.red {
|
||||||
background-color: #ee6055;
|
background-color: #ee6055;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
.red-loader {
|
|
||||||
|
.red-loader {
|
||||||
background-color: #fa8383;
|
background-color: #fa8383;
|
||||||
}
|
}
|
||||||
|
|
||||||
.yellow{
|
.yellow {
|
||||||
background-color: #FF9D23;
|
background-color: #ff9d23;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
.yellow-loader{
|
|
||||||
|
.yellow-loader {
|
||||||
background-color: #ffbf81;
|
background-color: #ffbf81;
|
||||||
}
|
}
|
||||||
|
|
||||||
.blue {
|
.blue {
|
||||||
background-color: #809bce;
|
background-color: #809bce;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
.blue-loader{
|
|
||||||
background-color: #95b8d1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.green {
|
.blue-loader {
|
||||||
|
background-color: #95b8d1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.green {
|
||||||
background-color: green;
|
background-color: green;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
.green-loader {
|
|
||||||
background-color: darkgreen;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading {
|
.green-loader {
|
||||||
|
background-color: darkgreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@ -74,33 +92,33 @@ defineProps({
|
|||||||
height: 1em;
|
height: 1em;
|
||||||
width: 0;
|
width: 0;
|
||||||
animation: loading 4s linear forwards;
|
animation: loading 4s linear forwards;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Animation for the loading bar */
|
/* Animation for the loading bar */
|
||||||
@keyframes loading {
|
@keyframes loading {
|
||||||
0% {
|
0% {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
100% {
|
100% {
|
||||||
width: 0;
|
width: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes disappear {
|
@keyframes disappear {
|
||||||
0% {
|
0% {
|
||||||
height: 100px;
|
height: 100px;
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
margin: 1em;
|
margin: 1em;
|
||||||
}
|
}
|
||||||
80% {
|
80% {
|
||||||
height: 100px;
|
height: 100px;
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
margin: 1em;
|
margin: 1em;
|
||||||
}
|
}
|
||||||
100% {
|
100% {
|
||||||
height: 0;
|
height: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,17 +1,16 @@
|
|||||||
import { createApp } from 'vue'
|
import { createApp } from "vue";
|
||||||
import App from './App.vue'
|
import App from "./App.vue";
|
||||||
import router from './router/router.ts'
|
import router from "./router/router.ts";
|
||||||
import {createPinia} from "pinia";
|
import { createPinia } from "pinia";
|
||||||
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate';
|
import piniaPluginPersistedstate from "pinia-plugin-persistedstate";
|
||||||
import keycloakService from './services/keycloak';
|
import keycloakService from "./services/keycloak";
|
||||||
import {type AuthStore, useAuthStore} from "@/stores/authStore.ts";
|
import { type AuthStore, useAuthStore } from "@/stores/authStore.ts";
|
||||||
|
|
||||||
|
|
||||||
let store: AuthStore;
|
let store: AuthStore;
|
||||||
|
|
||||||
keycloakService.CallInit(() => {
|
keycloakService.CallInit(() => {
|
||||||
try {
|
try {
|
||||||
const app = createApp(App)
|
const app = createApp(App);
|
||||||
|
|
||||||
// Setup pinia store, allowing user to keep logged in status after refresh
|
// Setup pinia store, allowing user to keep logged in status after refresh
|
||||||
const pinia = createPinia();
|
const pinia = createPinia();
|
||||||
@ -19,19 +18,14 @@ keycloakService.CallInit(() => {
|
|||||||
app.use(pinia);
|
app.use(pinia);
|
||||||
store = useAuthStore();
|
store = useAuthStore();
|
||||||
keycloakService.CallInitStore(store);
|
keycloakService.CallInitStore(store);
|
||||||
app.use(router)
|
app.use(router);
|
||||||
|
|
||||||
app.mount('#app');
|
app.mount("#app");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Error while initiating Keycloak.")
|
console.error("Error while initiating Keycloak.");
|
||||||
console.error(e)
|
console.error(e);
|
||||||
createApp(App).mount('#app');
|
createApp(App).mount("#app");
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
})
|
export { store };
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export {store};
|
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
import { createRouter, createWebHistory } from 'vue-router'
|
import { createRouter, createWebHistory } from "vue-router";
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(import.meta.env.BASE_URL),
|
history: createWebHistory(import.meta.env.BASE_URL),
|
||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
path: '/test',
|
path: "/test",
|
||||||
name: 'test',
|
name: "test",
|
||||||
// route level code-splitting
|
// route level code-splitting
|
||||||
// this generates a separate chunk (About.[hash].js) for this route
|
// this generates a separate chunk (About.[hash].js) for this route
|
||||||
// which is lazy-loaded when the route is visited.
|
// which is lazy-loaded when the route is visited.
|
||||||
component: () => import('../views/testComponent.vue'),
|
component: () => import("../views/testComponent.vue"),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
});
|
||||||
|
|
||||||
export default router
|
export default router;
|
||||||
|
@ -1,31 +1,36 @@
|
|||||||
import axios, {type AxiosError, type AxiosResponse} from "axios";
|
import axios, { type AxiosError, type AxiosResponse } from "axios";
|
||||||
import {store} from "@/main.ts";
|
import { store } from "@/main.ts";
|
||||||
import {addNewMessage, color} from "@/services/popupDisplayer.ts";
|
import { addNewMessage, color } from "@/services/popupDisplayer.ts";
|
||||||
|
|
||||||
const axiosInstance = axios.create({
|
const axiosInstance = axios.create({
|
||||||
baseURL: import.meta.env.VITE_BACKEND_URL,
|
baseURL: import.meta.env.VITE_BACKEND_URL,
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
axiosInstance.interceptors.response.use(
|
axiosInstance.interceptors.response.use(
|
||||||
response => response, // Directly return successful responses.
|
(response) => response, // Directly return successful responses.
|
||||||
async error => {
|
async (error) => {
|
||||||
const originalRequest = error.config;
|
const originalRequest = error.config;
|
||||||
if (error.response.status === 401 && !originalRequest._retry && store.authenticated) {
|
if (
|
||||||
|
error.response.status === 401 &&
|
||||||
|
!originalRequest._retry &&
|
||||||
|
store.authenticated
|
||||||
|
) {
|
||||||
originalRequest._retry = true; // Mark the request as retried to avoid infinite loops.
|
originalRequest._retry = true; // Mark the request as retried to avoid infinite loops.
|
||||||
try {
|
try {
|
||||||
await store.refreshUserToken();
|
await store.refreshUserToken();
|
||||||
// Update the authorization header with the new access token.
|
// 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.
|
return axiosInstance(originalRequest); // Retry the original request with the new access token.
|
||||||
} catch (refreshError) {
|
} catch (refreshError) {
|
||||||
// Handle refresh token errors by clearing stored tokens and redirecting to the login page.
|
// Handle refresh token errors by clearing stored tokens and redirecting to the login page.
|
||||||
console.error('Token refresh failed:', refreshError);
|
console.error("Token refresh failed:", refreshError);
|
||||||
localStorage.removeItem('accessToken');
|
localStorage.removeItem("accessToken");
|
||||||
localStorage.removeItem('refreshToken');
|
localStorage.removeItem("refreshToken");
|
||||||
window.location.href = '/login';
|
window.location.href = "/login";
|
||||||
return Promise.reject(refreshError);
|
return Promise.reject(refreshError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -34,20 +39,29 @@ axiosInstance.interceptors.response.use(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// TODO: spawn a error modal
|
// TODO: spawn a error modal
|
||||||
function defaultApiErrorHandler(err: AxiosError){
|
function defaultApiErrorHandler(err: AxiosError) {
|
||||||
addNewMessage(err.message, color.Red);
|
addNewMessage(err.message, color.Red);
|
||||||
}
|
}
|
||||||
|
|
||||||
function defaultApiSuccessHandler(response: AxiosResponse){
|
function defaultApiSuccessHandler(response: AxiosResponse) {
|
||||||
addNewMessage(response.data, color.Green)
|
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
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 };
|
||||||
|
@ -1,34 +1,31 @@
|
|||||||
import Keycloak from 'keycloak-js';
|
import Keycloak from "keycloak-js";
|
||||||
import type {AuthStore} from "@/stores/authStore.ts";
|
import type { AuthStore } from "@/stores/authStore.ts";
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
url: import.meta.env.VITE_KEYCLOAK_URL,
|
url: import.meta.env.VITE_KEYCLOAK_URL,
|
||||||
clientId: import.meta.env.VITE_KEYCLOAK_CLIENT_ID,
|
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);
|
const keycloak = new Keycloak(options);
|
||||||
let authenticated: boolean | undefined;
|
let authenticated: boolean | undefined;
|
||||||
let store = null;
|
let store = null;
|
||||||
|
|
||||||
async function login(){
|
async function login() {
|
||||||
try {
|
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;
|
return keycloak;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function signup(){
|
async function signup() {
|
||||||
try {
|
try {
|
||||||
await keycloak.login(
|
await keycloak.login({ action: "register" }); // https://www.keycloak.org/securing-apps/javascript-adapter#:~:text=when%20initialization%20completes.-,login(options),-Redirects%20to%20login
|
||||||
{action: "register"}
|
|
||||||
) // https://www.keycloak.org/securing-apps/javascript-adapter#:~:text=when%20initialization%20completes.-,login(options),-Redirects%20to%20login
|
|
||||||
return keycloak;
|
return keycloak;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,13 +40,13 @@ async function init(onInitCallback: () => void) {
|
|||||||
onLoad: "check-sso",
|
onLoad: "check-sso",
|
||||||
silentCheckSsoRedirectUri: `${location.origin}/silent-check-sso.htm`,
|
silentCheckSsoRedirectUri: `${location.origin}/silent-check-sso.htm`,
|
||||||
responseMode: "query",
|
responseMode: "query",
|
||||||
})
|
});
|
||||||
onInitCallback()
|
onInitCallback();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Keycloak init failed")
|
console.error("Keycloak init failed");
|
||||||
console.error(error)
|
console.error(error);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes store with Keycloak user data
|
* Initializes store with Keycloak user data
|
||||||
@ -57,17 +54,19 @@ async function init(onInitCallback: () => void) {
|
|||||||
*/
|
*/
|
||||||
async function initStore(storeInstance: AuthStore) {
|
async function initStore(storeInstance: AuthStore) {
|
||||||
try {
|
try {
|
||||||
store = storeInstance
|
store = storeInstance;
|
||||||
console.log(keycloak)
|
console.log(keycloak);
|
||||||
await store.initOauth(keycloak)
|
await store.initOauth(keycloak);
|
||||||
|
|
||||||
// Show alert if user is not authenticated
|
// Show alert if user is not authenticated
|
||||||
if (!authenticated) { console.warn("not authenticated") }
|
if (!authenticated) {
|
||||||
|
console.warn("not authenticated");
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Keycloak init failed")
|
console.error("Keycloak init failed");
|
||||||
console.error(error)
|
console.error(error);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logout user
|
* Logout user
|
||||||
@ -84,7 +83,7 @@ async function refreshToken() {
|
|||||||
await keycloak.updateToken(480);
|
await keycloak.updateToken(480);
|
||||||
return keycloak;
|
return keycloak;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to refresh token');
|
console.error("Failed to refresh token");
|
||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -98,4 +97,4 @@ const KeycloakService = {
|
|||||||
callSignup: signup,
|
callSignup: signup,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default KeycloakService;
|
export default KeycloakService;
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
import {ref} from "vue";
|
import { ref, type Ref } from "vue";
|
||||||
import {type Ref} from "vue";
|
|
||||||
enum color {Red, Yellow, Blue, Green}
|
enum color {
|
||||||
|
Red,
|
||||||
|
Yellow,
|
||||||
|
Blue,
|
||||||
|
Green,
|
||||||
|
}
|
||||||
|
|
||||||
type ErrorMessageContent = {
|
type ErrorMessageContent = {
|
||||||
message: string;
|
message: string;
|
||||||
@ -11,23 +16,28 @@ type ErrorMessageContent = {
|
|||||||
|
|
||||||
let id: number = 0;
|
let id: number = 0;
|
||||||
const getId = () => {
|
const getId = () => {
|
||||||
id = id+1;
|
id = id + 1;
|
||||||
return id;
|
return id;
|
||||||
}
|
};
|
||||||
|
|
||||||
function addNewMessage(errorMessage: string, type?: color, timeout?: number){
|
function addNewMessage(errorMessage: string, type?: color, timeout?: number) {
|
||||||
if (timeout == null){
|
if (timeout == null) {
|
||||||
timeout = 5000;
|
timeout = 5000;
|
||||||
}
|
}
|
||||||
if (type == null){
|
if (type == null) {
|
||||||
type = color.Red;
|
type = color.Red;
|
||||||
}
|
}
|
||||||
|
|
||||||
const data: ErrorMessageContent = {message: errorMessage, timeout: timeout, color: type, id: getId()};
|
const data: ErrorMessageContent = {
|
||||||
errorList.value.push(data)
|
message: errorMessage,
|
||||||
setTimeout(() => errorList.value.slice(0, 1), timeout)
|
timeout: timeout,
|
||||||
|
color: type,
|
||||||
|
id: getId(),
|
||||||
|
};
|
||||||
|
errorList.value.push(data);
|
||||||
|
setTimeout(() => errorList.value.slice(0, 1), timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
const errorList: Ref<ErrorMessageContent[]>= ref([])
|
const errorList: Ref<ErrorMessageContent[]> = ref([]);
|
||||||
|
|
||||||
export {addNewMessage, errorList, color, type ErrorMessageContent}
|
export { addNewMessage, errorList, color, type ErrorMessageContent };
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { defineStore } from "pinia";
|
import { defineStore } from "pinia";
|
||||||
import keycloakService from '@/services/keycloak';
|
import keycloakService from "@/services/keycloak";
|
||||||
import type Keycloak from "keycloak-js";
|
import type Keycloak from "keycloak-js";
|
||||||
|
|
||||||
const useAuthStore = defineStore("storeAuth", {
|
const useAuthStore = defineStore("storeAuth", {
|
||||||
@ -11,44 +11,51 @@ const useAuthStore = defineStore("storeAuth", {
|
|||||||
refreshToken: "",
|
refreshToken: "",
|
||||||
username: "",
|
username: "",
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
persist: true,
|
persist: true,
|
||||||
getters: {},
|
getters: {},
|
||||||
actions: {
|
actions: {
|
||||||
// Initialize Keycloak OAuth
|
// Initialize Keycloak OAuth
|
||||||
async initOauth(keycloak: Keycloak, clearData = true) {
|
async initOauth(keycloak: Keycloak, clearData = true) {
|
||||||
if(clearData) { await this.clearUserData(); }
|
if (clearData) {
|
||||||
|
await this.clearUserData();
|
||||||
|
}
|
||||||
|
|
||||||
this.authenticated = !!keycloak.authenticated; // the !! removes undefined
|
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.username = keycloak.idTokenParsed.given_name;
|
||||||
this.user.token = keycloak.token;
|
this.user.token = keycloak.token;
|
||||||
this.user.refreshToken = keycloak.refreshToken;
|
this.user.refreshToken = keycloak.refreshToken;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async login(){
|
async login() {
|
||||||
try {
|
try {
|
||||||
const keycloak = await keycloakService.callLogin();
|
const keycloak = await keycloakService.callLogin();
|
||||||
if (keycloak)
|
if (keycloak) await this.initOauth(keycloak);
|
||||||
await this.initOauth(keycloak);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async signup() {
|
async signup() {
|
||||||
try {
|
try {
|
||||||
const keycloak = await keycloakService.callSignup();
|
const keycloak = await keycloakService.callSignup();
|
||||||
if (keycloak)
|
if (keycloak) await this.initOauth(keycloak);
|
||||||
await this.initOauth(keycloak);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// Logout user
|
// Logout user
|
||||||
async logout() {
|
async logout() {
|
||||||
try {
|
try {
|
||||||
await keycloakService.CallLogout(import.meta.env.VITE_APP_URL + "/test");
|
await keycloakService.CallLogout(
|
||||||
|
import.meta.env.VITE_APP_URL + "/test"
|
||||||
|
);
|
||||||
await this.clearUserData();
|
await this.clearUserData();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
@ -58,8 +65,7 @@ const useAuthStore = defineStore("storeAuth", {
|
|||||||
async refreshUserToken() {
|
async refreshUserToken() {
|
||||||
try {
|
try {
|
||||||
const keycloak = await keycloakService.CallTokenRefresh();
|
const keycloak = await keycloakService.CallTokenRefresh();
|
||||||
if (keycloak)
|
if (keycloak) await this.initOauth(keycloak, false);
|
||||||
await this.initOauth(keycloak, false);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
@ -69,13 +75,13 @@ const useAuthStore = defineStore("storeAuth", {
|
|||||||
this.authenticated = false;
|
this.authenticated = false;
|
||||||
this.user = {
|
this.user = {
|
||||||
token: "",
|
token: "",
|
||||||
refreshToken: "",
|
refreshToken: "",
|
||||||
username: "",
|
username: "",
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
type AuthStore = ReturnType<typeof useAuthStore>
|
type AuthStore = ReturnType<typeof useAuthStore>;
|
||||||
|
|
||||||
export {useAuthStore, type AuthStore}
|
export { useAuthStore, type AuthStore };
|
||||||
|
@ -1,22 +1,25 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { errorList } from "@/services/popupDisplayer.ts";
|
||||||
import {errorList} from "@/services/popupDisplayer.ts";
|
|
||||||
import ErrorModal from "@/components/errorModal.vue";
|
import ErrorModal from "@/components/errorModal.vue";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="error-wrapper">
|
<div class="error-wrapper">
|
||||||
<error-modal v-for="elm in errorList" :key="elm.id" :error-message=elm.message :error-color="elm.color"></error-modal>
|
<error-modal
|
||||||
</div>
|
v-for="elm in errorList"
|
||||||
|
:key="elm.id"
|
||||||
|
:error-message="elm.message"
|
||||||
|
:error-color="elm.color"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.error-wrapper{
|
.error-wrapper {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 70%;
|
left: 70%;
|
||||||
//background-color: blue;
|
//background-color: blue;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 30%;
|
width: 30%;
|
||||||
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,66 +1,81 @@
|
|||||||
<script setup lang="ts">
|
<script lang="ts" setup>
|
||||||
import {store} from "../main.ts";
|
import { store } from "../main.ts";
|
||||||
import {callApi} from "@/services/api.ts";
|
import { callApi } from "@/services/api.ts";
|
||||||
import TempModal from "@/components/temp-modal.vue";
|
import TempModal from "@/components/temp-modal.vue";
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<h1>Test page</h1>
|
<h1>Test page</h1>
|
||||||
<table class="test" style="width:100%">
|
<table class="test" style="width: 100%">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Is Currently Authenticated ? </td>
|
<td>Is Currently Authenticated ?</td>
|
||||||
<td>{{store.authenticated}}</td>
|
<td>{{ store.authenticated }}</td>
|
||||||
<td><button @click="store.login">Login</button></td>
|
<td>
|
||||||
<td><button @click="store.logout">Logout</button></td>
|
<button @click="store.login">Login</button>
|
||||||
<td><button @click="store.signup">Signup</button></td>
|
</td>
|
||||||
</tr>
|
<td>
|
||||||
<tr>
|
<button @click="store.logout">Logout</button>
|
||||||
<td>current token</td>
|
</td>
|
||||||
<td>{{store.user.token}}</td>
|
<td>
|
||||||
<td><button @click="store.refreshUserToken">Refresh</button></td>
|
<button @click="store.signup">Signup</button>
|
||||||
</tr>
|
</td>
|
||||||
<tr>
|
</tr>
|
||||||
<td>Current refresh token</td>
|
<tr>
|
||||||
<td>{{store.user.refreshToken}}</td>
|
<td>current token</td>
|
||||||
</tr>
|
<td>{{ store.user.token }}</td>
|
||||||
<tr>
|
<td>
|
||||||
<td>Entrepreneur API call</td>
|
<button @click="store.refreshUserToken">Refresh</button>
|
||||||
<td><button @click="callApi('random')">call</button></td>
|
</td>
|
||||||
<td>res</td>
|
</tr>
|
||||||
<td></td>
|
<tr>
|
||||||
</tr>
|
<td>Current refresh token</td>
|
||||||
<tr>
|
<td>{{ store.user.refreshToken }}</td>
|
||||||
<td>Admin API call</td>
|
</tr>
|
||||||
<td><button @click="callApi('random2')">call</button></td>
|
<tr>
|
||||||
<td>res</td>
|
<td>Entrepreneur API call</td>
|
||||||
<td></td>
|
<td>
|
||||||
</tr>
|
<button @click="callApi('random')">call</button>
|
||||||
<tr>
|
</td>
|
||||||
<td>Unauth API call</td>
|
<td>res</td>
|
||||||
<td><button @click="callApi('random3')">call</button></td>
|
<td></td>
|
||||||
<td>res</td>
|
</tr>
|
||||||
<td id="3"></td>
|
<tr>
|
||||||
</tr>
|
<td>Admin API call</td>
|
||||||
</tbody>
|
<td>
|
||||||
</table>
|
<button @click="callApi('random2')">call</button>
|
||||||
<temp-modal></temp-modal>
|
</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>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
table {
|
table {
|
||||||
width: 100px;
|
width: 100px;
|
||||||
table-layout: fixed
|
table-layout: fixed;
|
||||||
}
|
}
|
||||||
tr {
|
|
||||||
|
tr {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
td {
|
|
||||||
|
td {
|
||||||
border: solid 1px black;
|
border: solid 1px black;
|
||||||
width: 20%;
|
width: 20%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user