Compare commits
6 Commits
feature/mo
...
feature/fr
| Author | SHA1 | Date | |
|---|---|---|---|
| 467f5a9193 | |||
| 695342e19a | |||
| 6be93d915e | |||
| bbf58ecb19 | |||
| f1468ede1a | |||
| 51df03a76c |
24
front-nuxt/.gitignore
vendored
Normal file
24
front-nuxt/.gitignore
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
# Nuxt dev/build outputs
|
||||
.output
|
||||
.data
|
||||
.nuxt
|
||||
.nitro
|
||||
.cache
|
||||
dist
|
||||
|
||||
# Node dependencies
|
||||
node_modules
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
|
||||
# Misc
|
||||
.DS_Store
|
||||
.fleet
|
||||
.idea
|
||||
|
||||
# Local env files
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
5
front-nuxt/README.md
Normal file
5
front-nuxt/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Nuxt Front-end
|
||||
|
||||
## Global state management
|
||||
|
||||
- `xstate`: for complex state machines.
|
||||
2
front-nuxt/app/app.config.ts
Normal file
2
front-nuxt/app/app.config.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export default defineAppConfig({
|
||||
})
|
||||
5
front-nuxt/app/app.vue
Normal file
5
front-nuxt/app/app.vue
Normal file
@@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<NuxtLayout>
|
||||
<NuxtPage />
|
||||
</NuxtLayout>
|
||||
</template>
|
||||
13
front-nuxt/app/components/auth/login-form.vue
Normal file
13
front-nuxt/app/components/auth/login-form.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
const props = defineProps<{
|
||||
onSubmitAction: (credentials: {
|
||||
username: string;
|
||||
password: string;
|
||||
}) => boolean;
|
||||
}>();
|
||||
async function onSubmit() {}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<form @onsubmit="onSubmit">Test form</form>
|
||||
</template>
|
||||
1
front-nuxt/app/constants/session.constants.ts
Normal file
1
front-nuxt/app/constants/session.constants.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const USER_SESSION_STORAGE_KEY = "userSession";
|
||||
14
front-nuxt/app/error.vue
Normal file
14
front-nuxt/app/error.vue
Normal file
@@ -0,0 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import type { NuxtError } from "#app";
|
||||
|
||||
const { error } = defineProps({
|
||||
error: Object as () => NuxtError,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h1>{{ error?.statusCode }}</h1>
|
||||
<NuxtLink to="/">Go back home</NuxtLink>
|
||||
</div>
|
||||
</template>
|
||||
6
front-nuxt/app/layouts/auth.vue
Normal file
6
front-nuxt/app/layouts/auth.vue
Normal file
@@ -0,0 +1,6 @@
|
||||
<template>
|
||||
<header>
|
||||
Loging header actions
|
||||
</header>
|
||||
<slot/>
|
||||
</template>
|
||||
7
front-nuxt/app/layouts/default.vue
Normal file
7
front-nuxt/app/layouts/default.vue
Normal file
@@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<main>
|
||||
<header>Header</header>
|
||||
<slot />
|
||||
<footer>Footer</footer>
|
||||
</main>
|
||||
</template>
|
||||
46
front-nuxt/app/middleware/auth.global.ts
Normal file
46
front-nuxt/app/middleware/auth.global.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { USER_SESSION_STORAGE_KEY } from "~/constants/session.constants";
|
||||
import type { User } from "~/stores/user";
|
||||
|
||||
const LOGIN_ROUTE = "/login";
|
||||
|
||||
export default defineNuxtRouteMiddleware((to, from) => {
|
||||
let session: string | null = null;
|
||||
|
||||
// Not secured route// Recover user session
|
||||
if (
|
||||
to.path.startsWith("/favicon.ico") ||
|
||||
to.path.startsWith("/assets/") ||
|
||||
to.path.startsWith(LOGIN_ROUTE)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Recover user session
|
||||
// NOTE: using the sessionStorage will not allow the user to have multiple tabs, to communicate them one can use the
|
||||
// broadcast channel, although its not the safest way: https://developer.mozilla.org/en-US/docs/Web/API/Broadcast_Channel_API
|
||||
try {
|
||||
session = sessionStorage.getItem(
|
||||
USER_SESSION_STORAGE_KEY,
|
||||
);
|
||||
|
||||
// Manage secured routes
|
||||
if (session) {
|
||||
const user = JSON.parse(session) as User;
|
||||
if (
|
||||
user.apiSession?.exp &&
|
||||
user.apiSession.exp > Date.now()
|
||||
) {
|
||||
// try refresh token if it exists
|
||||
// else just redirect to login
|
||||
return navigateTo(LOGIN_ROUTE);
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
return navigateTo(LOGIN_ROUTE);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(">> [!] Not in client", e);
|
||||
}
|
||||
|
||||
console.log(">> Global Middleware from ", from.path);
|
||||
});
|
||||
3
front-nuxt/app/middleware/login.ts
Normal file
3
front-nuxt/app/middleware/login.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export default defineNuxtRouteMiddleware((to, from) => {
|
||||
console.log(">> Login middleware from ", from.path);
|
||||
});
|
||||
5
front-nuxt/app/pages/about.vue
Normal file
5
front-nuxt/app/pages/about.vue
Normal file
@@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
About
|
||||
</div>
|
||||
</template>
|
||||
3
front-nuxt/app/pages/index.vue
Normal file
3
front-nuxt/app/pages/index.vue
Normal file
@@ -0,0 +1,3 @@
|
||||
<template>
|
||||
<div>Main page</div>
|
||||
</template>
|
||||
23
front-nuxt/app/pages/login.vue
Normal file
23
front-nuxt/app/pages/login.vue
Normal file
@@ -0,0 +1,23 @@
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
layout: "auth",
|
||||
middleware: "login",
|
||||
});
|
||||
|
||||
const userStore = useUserStore();
|
||||
const setUpUser = () => {
|
||||
// userStore.setUser({
|
||||
// id: "1",
|
||||
// name: "My name",
|
||||
// roles: ["user", "manager"],
|
||||
// });
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
Login form
|
||||
<AuthLoginForm />
|
||||
<button @click="setUpUser">Login</button>
|
||||
</div>
|
||||
</template>
|
||||
10
front-nuxt/app/plugins/log-errors.client.ts
Normal file
10
front-nuxt/app/plugins/log-errors.client.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
export default defineNuxtPlugin((nuxtApp) => {
|
||||
nuxtApp.vueApp.config.errorHandler = (error, instance, info) => {
|
||||
// handle error, e.g. report to a service
|
||||
}
|
||||
|
||||
// Also possible
|
||||
nuxtApp.hook('vue:error', (error, instance, info) => {
|
||||
// handle error, e.g. report to a service
|
||||
})
|
||||
})
|
||||
57
front-nuxt/app/stores/user.ts
Normal file
57
front-nuxt/app/stores/user.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import { USER_SESSION_STORAGE_KEY } from "~/constants/session.constants";
|
||||
|
||||
type Role = "user" | "manager" | "admin";
|
||||
|
||||
interface ApiSession {
|
||||
exp?: number;
|
||||
accessToken: string;
|
||||
refreshToken?: string;
|
||||
}
|
||||
|
||||
export interface User {
|
||||
id: string;
|
||||
roles: Role[];
|
||||
image?: string;
|
||||
name?: string;
|
||||
apiSession: ApiSession;
|
||||
}
|
||||
|
||||
export const useUserStore = defineStore("user", {
|
||||
state: (): User => ({
|
||||
id: "-1",
|
||||
roles: [],
|
||||
apiSession: { accessToken: "" },
|
||||
}),
|
||||
getters: {
|
||||
profile: (state) => ({
|
||||
id: state.id,
|
||||
name: state.name,
|
||||
image: state.image,
|
||||
roles: state.roles,
|
||||
}),
|
||||
token: (state) => state.apiSession?.accessToken,
|
||||
},
|
||||
actions: {
|
||||
persist() {
|
||||
try {
|
||||
sessionStorage.setItem(
|
||||
USER_SESSION_STORAGE_KEY,
|
||||
JSON.stringify(this.token),
|
||||
);
|
||||
} catch (e) {
|
||||
console.error(
|
||||
">> [!] user-store - Sessions storage access on server",
|
||||
e,
|
||||
);
|
||||
}
|
||||
},
|
||||
setUser(user: User) {
|
||||
this.$state = user;
|
||||
this.persist();
|
||||
},
|
||||
updateToken(token: ApiSession["accessToken"]) {
|
||||
this.apiSession.accessToken = token;
|
||||
this.persist();
|
||||
},
|
||||
},
|
||||
});
|
||||
6
front-nuxt/eslint.config.mjs
Normal file
6
front-nuxt/eslint.config.mjs
Normal file
@@ -0,0 +1,6 @@
|
||||
// @ts-check
|
||||
import withNuxt from './.nuxt/eslint.config.mjs'
|
||||
|
||||
export default withNuxt(
|
||||
// Your custom configs here
|
||||
)
|
||||
14
front-nuxt/nuxt.config.ts
Normal file
14
front-nuxt/nuxt.config.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
// https://nuxt.com/docs/api/configuration/nuxt-config
|
||||
export default defineNuxtConfig({
|
||||
compatibilityDate: '2025-07-15',
|
||||
devtools: { enabled: true },
|
||||
modules: ['@nuxt/eslint', '@nuxt/image', '@nuxt/test-utils', '@pinia/nuxt'],
|
||||
vite: {
|
||||
vue: {
|
||||
customElement: true,
|
||||
},
|
||||
vueJsx: {
|
||||
mergeProps: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
13526
front-nuxt/package-lock.json
generated
Normal file
13526
front-nuxt/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
25
front-nuxt/package.json
Normal file
25
front-nuxt/package.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "nuxt-app",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "nuxt build",
|
||||
"dev": "nuxt dev",
|
||||
"generate": "nuxt generate",
|
||||
"preview": "nuxt preview",
|
||||
"postinstall": "nuxt prepare"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nuxt/eslint": "^1.9.0",
|
||||
"@nuxt/image": "^1.11.0",
|
||||
"@nuxt/test-utils": "^3.19.2",
|
||||
"@pinia/nuxt": "^0.11.2",
|
||||
"@xstate/vue": "^5.0.0",
|
||||
"eslint": "^9.36.0",
|
||||
"nuxt": "^4.1.2",
|
||||
"pinia": "^3.0.3",
|
||||
"vue": "^3.5.21",
|
||||
"vue-router": "^4.5.1",
|
||||
"xstate": "^5.22.0"
|
||||
}
|
||||
}
|
||||
BIN
front-nuxt/public/favicon.ico
Normal file
BIN
front-nuxt/public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
2
front-nuxt/public/robots.txt
Normal file
2
front-nuxt/public/robots.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
User-Agent: *
|
||||
Disallow:
|
||||
3
front-nuxt/server/api/test.ts
Normal file
3
front-nuxt/server/api/test.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export default defineEventHandler(async (event) => {
|
||||
// ... Do whatever you want here
|
||||
})
|
||||
7
front-nuxt/server/plugins/define-metas.ts
Normal file
7
front-nuxt/server/plugins/define-metas.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export default defineNitroPlugin((nitroApp) => {
|
||||
nitroApp.hooks.hook('render:html', (html, { event }) => {
|
||||
html.head.push(`<meta name="description" content="This is an example description." />`)
|
||||
})
|
||||
// You can also intercept the response here.
|
||||
// nitroApp.hooks.hook('render:response', (response, { event }) => { console.log(response) })
|
||||
})
|
||||
18
front-nuxt/tsconfig.json
Normal file
18
front-nuxt/tsconfig.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
// https://nuxt.com/docs/guide/concepts/typescript
|
||||
"files": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./.nuxt/tsconfig.app.json"
|
||||
},
|
||||
{
|
||||
"path": "./.nuxt/tsconfig.server.json"
|
||||
},
|
||||
{
|
||||
"path": "./.nuxt/tsconfig.shared.json"
|
||||
},
|
||||
{
|
||||
"path": "./.nuxt/tsconfig.node.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
4736
front/package-lock.json
generated
4736
front/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user