6 Commits

Author SHA1 Message Date
467f5a9193 feat: minor format + upgrade
Some checks failed
Lint front projects / Next front lint (push) Has been cancelled
Test front projects / Next front test (push) Has been cancelled
2026-02-01 12:19:00 +01:00
695342e19a feat(front-nuxt): [WIP] login 2025-11-01 21:30:24 +01:00
6be93d915e feat: auth session managed on sessionStorage 2025-10-31 19:55:22 +01:00
bbf58ecb19 feat: login store moved to sessionStorage 2025-10-31 00:24:20 +01:00
f1468ede1a feat: nuxt basics 2025-10-24 12:55:14 +02:00
51df03a76c feat: nuxt base 2025-10-12 18:46:12 +02:00
26 changed files with 16650 additions and 1911 deletions

24
front-nuxt/.gitignore vendored Normal file
View 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
View File

@@ -0,0 +1,5 @@
# Nuxt Front-end
## Global state management
- `xstate`: for complex state machines.

View File

@@ -0,0 +1,2 @@
export default defineAppConfig({
})

5
front-nuxt/app/app.vue Normal file
View File

@@ -0,0 +1,5 @@
<template>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</template>

View 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>

View File

@@ -0,0 +1 @@
export const USER_SESSION_STORAGE_KEY = "userSession";

14
front-nuxt/app/error.vue Normal file
View 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>

View File

@@ -0,0 +1,6 @@
<template>
<header>
Loging header actions
</header>
<slot/>
</template>

View File

@@ -0,0 +1,7 @@
<template>
<main>
<header>Header</header>
<slot />
<footer>Footer</footer>
</main>
</template>

View 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);
});

View File

@@ -0,0 +1,3 @@
export default defineNuxtRouteMiddleware((to, from) => {
console.log(">> Login middleware from ", from.path);
});

View File

@@ -0,0 +1,5 @@
<template>
<div>
About
</div>
</template>

View File

@@ -0,0 +1,3 @@
<template>
<div>Main page</div>
</template>

View 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>

View 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
})
})

View 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();
},
},
});

View 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
View 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
View File

File diff suppressed because it is too large Load Diff

25
front-nuxt/package.json Normal file
View 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"
}
}

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -0,0 +1,2 @@
User-Agent: *
Disallow:

View File

@@ -0,0 +1,3 @@
export default defineEventHandler(async (event) => {
// ... Do whatever you want here
})

View 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
View 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
View File

File diff suppressed because it is too large Load Diff