Commit
•
bd9595b
1
Parent(s):
113d498
🛂 Support authentication
Browse files- package.json +2 -0
- pnpm-lock.yaml +13 -0
- src/app.d.ts +12 -5
- src/hooks.server.ts +14 -0
- src/routes/+layout.server.ts +4 -2
- src/routes/+layout.svelte +18 -9
- src/routes/connexion/+layout.server.ts +10 -0
- src/routes/connexion/+page.server.ts +10 -4
- src/routes/deconnexion/+page.server.ts +8 -0
package.json
CHANGED
@@ -16,6 +16,7 @@
|
|
16 |
"@iconify-json/il": "^1.1.2",
|
17 |
"@sveltejs/adapter-node": "next",
|
18 |
"@sveltejs/kit": "next",
|
|
|
19 |
"@types/lodash": "^4.14.188",
|
20 |
"@types/marked": "^4.0.7",
|
21 |
"@typescript-eslint/eslint-plugin": "^5.27.0",
|
@@ -40,6 +41,7 @@
|
|
40 |
"type": "module",
|
41 |
"dependencies": {
|
42 |
"bcryptjs": "^2.4.3",
|
|
|
43 |
"form-data": "^4.0.0",
|
44 |
"lodash": "^4.17.21",
|
45 |
"mailgun.js": "^8.0.2",
|
|
|
16 |
"@iconify-json/il": "^1.1.2",
|
17 |
"@sveltejs/adapter-node": "next",
|
18 |
"@sveltejs/kit": "next",
|
19 |
+
"@types/bcryptjs": "^2.4.2",
|
20 |
"@types/lodash": "^4.14.188",
|
21 |
"@types/marked": "^4.0.7",
|
22 |
"@typescript-eslint/eslint-plugin": "^5.27.0",
|
|
|
41 |
"type": "module",
|
42 |
"dependencies": {
|
43 |
"bcryptjs": "^2.4.3",
|
44 |
+
"date-fns": "^2.29.3",
|
45 |
"form-data": "^4.0.0",
|
46 |
"lodash": "^4.17.21",
|
47 |
"mailgun.js": "^8.0.2",
|
pnpm-lock.yaml
CHANGED
@@ -5,6 +5,7 @@ specifiers:
|
|
5 |
'@iconify-json/il': ^1.1.2
|
6 |
'@sveltejs/adapter-node': next
|
7 |
'@sveltejs/kit': next
|
|
|
8 |
'@types/lodash': ^4.14.188
|
9 |
'@types/marked': ^4.0.7
|
10 |
'@typescript-eslint/eslint-plugin': ^5.27.0
|
@@ -13,6 +14,7 @@ specifiers:
|
|
13 |
'@unocss/preset-uno': ^0.46.3
|
14 |
'@unocss/reset': ^0.46.3
|
15 |
bcryptjs: ^2.4.3
|
|
|
16 |
eslint: ^8.16.0
|
17 |
eslint-config-prettier: ^8.3.0
|
18 |
eslint-plugin-svelte3: ^4.0.0
|
@@ -34,6 +36,7 @@ specifiers:
|
|
34 |
|
35 |
dependencies:
|
36 |
bcryptjs: 2.4.3
|
|
|
37 |
form-data: 4.0.0
|
38 |
lodash: 4.17.21
|
39 |
mailgun.js: 8.0.2
|
@@ -45,6 +48,7 @@ devDependencies:
|
|
45 |
'@iconify-json/il': 1.1.2
|
46 |
'@sveltejs/adapter-node': 1.0.0-next.100
|
47 |
'@sveltejs/kit': [email protected][email protected]
|
|
|
48 |
'@types/lodash': 4.14.188
|
49 |
'@types/marked': 4.0.7
|
50 |
'@typescript-eslint/eslint-plugin': 5.42.0_6xw5wg2354iw4zujk2f3vyfrzu
|
@@ -1128,6 +1132,10 @@ packages:
|
|
1128 |
- supports-color
|
1129 |
dev: true
|
1130 |
|
|
|
|
|
|
|
|
|
1131 |
/@types/cookie/0.5.1:
|
1132 |
resolution: {integrity: sha512-COUnqfB2+ckwXXSFInsFdOAWQzCCx+a5hq2ruyj+Vjund94RJQd4LG2u9hnvJrTgunKAaax7ancBYlDrNYxA0g==}
|
1133 |
dev: true
|
@@ -1698,6 +1706,11 @@ packages:
|
|
1698 |
source-map-js: 1.0.2
|
1699 |
dev: true
|
1700 |
|
|
|
|
|
|
|
|
|
|
|
1701 |
/debug/4.3.4:
|
1702 |
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
|
1703 |
engines: {node: '>=6.0'}
|
|
|
5 |
'@iconify-json/il': ^1.1.2
|
6 |
'@sveltejs/adapter-node': next
|
7 |
'@sveltejs/kit': next
|
8 |
+
'@types/bcryptjs': ^2.4.2
|
9 |
'@types/lodash': ^4.14.188
|
10 |
'@types/marked': ^4.0.7
|
11 |
'@typescript-eslint/eslint-plugin': ^5.27.0
|
|
|
14 |
'@unocss/preset-uno': ^0.46.3
|
15 |
'@unocss/reset': ^0.46.3
|
16 |
bcryptjs: ^2.4.3
|
17 |
+
date-fns: ^2.29.3
|
18 |
eslint: ^8.16.0
|
19 |
eslint-config-prettier: ^8.3.0
|
20 |
eslint-plugin-svelte3: ^4.0.0
|
|
|
36 |
|
37 |
dependencies:
|
38 |
bcryptjs: 2.4.3
|
39 |
+
date-fns: 2.29.3
|
40 |
form-data: 4.0.0
|
41 |
lodash: 4.17.21
|
42 |
mailgun.js: 8.0.2
|
|
|
48 |
'@iconify-json/il': 1.1.2
|
49 |
'@sveltejs/adapter-node': 1.0.0-next.100
|
50 |
'@sveltejs/kit': [email protected][email protected]
|
51 |
+
'@types/bcryptjs': 2.4.2
|
52 |
'@types/lodash': 4.14.188
|
53 |
'@types/marked': 4.0.7
|
54 |
'@typescript-eslint/eslint-plugin': 5.42.0_6xw5wg2354iw4zujk2f3vyfrzu
|
|
|
1132 |
- supports-color
|
1133 |
dev: true
|
1134 |
|
1135 |
+
/@types/bcryptjs/2.4.2:
|
1136 |
+
resolution: {integrity: sha512-LiMQ6EOPob/4yUL66SZzu6Yh77cbzJFYll+ZfaPiPPFswtIlA/Fs1MzdKYA7JApHU49zQTbJGX3PDmCpIdDBRQ==}
|
1137 |
+
dev: true
|
1138 |
+
|
1139 |
/@types/cookie/0.5.1:
|
1140 |
resolution: {integrity: sha512-COUnqfB2+ckwXXSFInsFdOAWQzCCx+a5hq2ruyj+Vjund94RJQd4LG2u9hnvJrTgunKAaax7ancBYlDrNYxA0g==}
|
1141 |
dev: true
|
|
|
1706 |
source-map-js: 1.0.2
|
1707 |
dev: true
|
1708 |
|
1709 |
+
/date-fns/2.29.3:
|
1710 |
+
resolution: {integrity: sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==}
|
1711 |
+
engines: {node: '>=0.11'}
|
1712 |
+
dev: false
|
1713 |
+
|
1714 |
/debug/4.3.4:
|
1715 |
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
|
1716 |
engines: {node: '>=6.0'}
|
src/app.d.ts
CHANGED
@@ -1,9 +1,16 @@
|
|
1 |
// See https://kit.svelte.dev/docs/types#app
|
2 |
// for information about these interfaces
|
3 |
// and what to do when importing types
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9 |
}
|
|
|
1 |
// See https://kit.svelte.dev/docs/types#app
|
2 |
// for information about these interfaces
|
3 |
// and what to do when importing types
|
4 |
+
|
5 |
+
import type { User } from '$lib/types/User';
|
6 |
+
|
7 |
+
declare global {
|
8 |
+
declare namespace App {
|
9 |
+
interface Locals {
|
10 |
+
user: null | User;
|
11 |
+
}
|
12 |
+
// interface PageData {}
|
13 |
+
// interface Error {}
|
14 |
+
// interface Platform {}
|
15 |
+
}
|
16 |
}
|
src/hooks.server.ts
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { users } from '$lib/server/db';
|
2 |
+
import type { Handle } from '@sveltejs/kit';
|
3 |
+
|
4 |
+
export const handle: Handle = async ({ event, resolve }) => {
|
5 |
+
const token = event.cookies.get('bergereToken');
|
6 |
+
|
7 |
+
if (token) {
|
8 |
+
event.locals.user = await users.findOne({ token });
|
9 |
+
}
|
10 |
+
|
11 |
+
const response = await resolve(event);
|
12 |
+
|
13 |
+
return response;
|
14 |
+
};
|
src/routes/+layout.server.ts
CHANGED
@@ -16,12 +16,14 @@ export const load: LayoutServerLoad = async (input) => {
|
|
16 |
|
17 |
return {
|
18 |
pageData,
|
19 |
-
pictures: pics
|
|
|
20 |
};
|
21 |
}
|
22 |
|
23 |
return {
|
24 |
pageData: null,
|
25 |
-
pictures: [] as Picture[]
|
|
|
26 |
};
|
27 |
};
|
|
|
16 |
|
17 |
return {
|
18 |
pageData,
|
19 |
+
pictures: pics,
|
20 |
+
user: !!input.locals.user
|
21 |
};
|
22 |
}
|
23 |
|
24 |
return {
|
25 |
pageData: null,
|
26 |
+
pictures: [] as Picture[],
|
27 |
+
user: !!input.locals.user
|
28 |
};
|
29 |
};
|
src/routes/+layout.svelte
CHANGED
@@ -3,10 +3,15 @@
|
|
3 |
import { slide } from 'svelte/transition';
|
4 |
import '@unocss/reset/normalize.css';
|
5 |
import 'uno.css';
|
|
|
6 |
|
7 |
-
$: path = $page.route
|
|
|
|
|
|
|
|
|
|
|
8 |
|
9 |
-
let user: null = null;
|
10 |
let menuOpen = false;
|
11 |
let date = new Date();
|
12 |
</script>
|
@@ -24,15 +29,19 @@
|
|
24 |
<meta property="twitter:description" content={$page.data.description} />
|
25 |
{/if}
|
26 |
<meta property="og:type" content={$page.data.type || 'website'} />
|
27 |
-
|
28 |
<meta
|
29 |
property="og:image"
|
30 |
-
content="{
|
|
|
|
|
|
|
|
|
|
|
31 |
/>
|
32 |
-
<meta name="twitter:image" content="{origin}/photos/raw/{shownPicture.storage[0]._id}" />
|
33 |
<meta name="twitter:card" content="summary_large_image" />
|
34 |
-
{/if}
|
35 |
-
|
36 |
{#if $page.data.price}
|
37 |
<meta property="product:price:amount" content={$page.data.price} />
|
38 |
<meta property="product:price:currency" content="€" />
|
@@ -76,7 +85,7 @@
|
|
76 |
class:text-sunray={path === '/contact'}
|
77 |
class="hidden sm:inline py-4 grow hover:text-sunray">Contact</a
|
78 |
>
|
79 |
-
{#if user}
|
80 |
<a href="/compte" title="Espace client" class="hidden sm:inline grow text-sunray text-3xl">
|
81 |
<div class="inline-block i-ant-design-user-outlined" />
|
82 |
</a>
|
@@ -127,7 +136,7 @@
|
|
127 |
<a href="/contact" class:text-sunray={path === '/contact'} class="my-2 hover:text-sunray"
|
128 |
>Contact</a
|
129 |
>
|
130 |
-
{#if user}
|
131 |
<a
|
132 |
href="/compte"
|
133 |
title="Espace client"
|
|
|
3 |
import { slide } from 'svelte/transition';
|
4 |
import '@unocss/reset/normalize.css';
|
5 |
import 'uno.css';
|
6 |
+
import type { LayoutData } from './$types';
|
7 |
|
8 |
+
$: path = $page.route?.id ?? '';
|
9 |
+
|
10 |
+
export let data: LayoutData;
|
11 |
+
|
12 |
+
$: shownPicture =
|
13 |
+
data.pictures.find((p) => p.storage[0].width >= p.storage[0].height) ?? data.pictures[0];
|
14 |
|
|
|
15 |
let menuOpen = false;
|
16 |
let date = new Date();
|
17 |
</script>
|
|
|
29 |
<meta property="twitter:description" content={$page.data.description} />
|
30 |
{/if}
|
31 |
<meta property="og:type" content={$page.data.type || 'website'} />
|
32 |
+
{#if shownPicture}
|
33 |
<meta
|
34 |
property="og:image"
|
35 |
+
content="{$page.url.protocol}//{$page.url.host}/photos/raw/{shownPicture.storage.slice(-2)[0]
|
36 |
+
._id}"
|
37 |
+
/>
|
38 |
+
<meta
|
39 |
+
name="twitter:image"
|
40 |
+
content="{$page.url.protocol}//{$page.url.host}/photos/raw/{shownPicture.storage[0]._id}"
|
41 |
/>
|
|
|
42 |
<meta name="twitter:card" content="summary_large_image" />
|
43 |
+
{/if}
|
44 |
+
<meta property="og:url" content="{$page.url.protocol}//{$page.url.host}{path}" />
|
45 |
{#if $page.data.price}
|
46 |
<meta property="product:price:amount" content={$page.data.price} />
|
47 |
<meta property="product:price:currency" content="€" />
|
|
|
85 |
class:text-sunray={path === '/contact'}
|
86 |
class="hidden sm:inline py-4 grow hover:text-sunray">Contact</a
|
87 |
>
|
88 |
+
{#if data.user}
|
89 |
<a href="/compte" title="Espace client" class="hidden sm:inline grow text-sunray text-3xl">
|
90 |
<div class="inline-block i-ant-design-user-outlined" />
|
91 |
</a>
|
|
|
136 |
<a href="/contact" class:text-sunray={path === '/contact'} class="my-2 hover:text-sunray"
|
137 |
>Contact</a
|
138 |
>
|
139 |
+
{#if data.user}
|
140 |
<a
|
141 |
href="/compte"
|
142 |
title="Espace client"
|
src/routes/connexion/+layout.server.ts
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { redirect } from '@sveltejs/kit';
|
2 |
+
import type { LayoutServerLoad } from './$types';
|
3 |
+
|
4 |
+
export const load: LayoutServerLoad = (event) => {
|
5 |
+
if (event.locals.user) {
|
6 |
+
throw redirect(302, '/');
|
7 |
+
}
|
8 |
+
|
9 |
+
return {};
|
10 |
+
};
|
src/routes/connexion/+page.server.ts
CHANGED
@@ -2,11 +2,17 @@ import { error, redirect } from '@sveltejs/kit';
|
|
2 |
import { users } from '$lib/server/db';
|
3 |
import bcrypt from 'bcryptjs';
|
4 |
import type { Actions } from './$types';
|
|
|
5 |
|
6 |
export const actions: Actions = {
|
7 |
default: async (event) => {
|
8 |
const data = await event.request.formData();
|
9 |
-
|
|
|
|
|
|
|
|
|
|
|
10 |
|
11 |
const user = await users.findOne({ email }, { collation: { locale: 'en', strength: 1 } });
|
12 |
|
@@ -14,7 +20,7 @@ export const actions: Actions = {
|
|
14 |
throw error(404, "Utilisateur non trouvé pour l'email: " + email);
|
15 |
}
|
16 |
|
17 |
-
const password = data.get('password').trim();
|
18 |
|
19 |
if (!(await bcrypt.compare(password as string, user.hash))) {
|
20 |
throw error(401, 'Mauvais mot de passe');
|
@@ -32,11 +38,11 @@ export const actions: Actions = {
|
|
32 |
sameSite: 'lax',
|
33 |
secure: true,
|
34 |
httpOnly: true,
|
35 |
-
|
36 |
});
|
37 |
|
38 |
if (event.url.searchParams.get('suivant')) {
|
39 |
-
throw redirect(303, event.url.searchParams.get('suivant'));
|
40 |
}
|
41 |
|
42 |
return { success: true };
|
|
|
2 |
import { users } from '$lib/server/db';
|
3 |
import bcrypt from 'bcryptjs';
|
4 |
import type { Actions } from './$types';
|
5 |
+
import { addYears } from 'date-fns';
|
6 |
|
7 |
export const actions: Actions = {
|
8 |
default: async (event) => {
|
9 |
const data = await event.request.formData();
|
10 |
+
|
11 |
+
if (!data || !data.get('email') || !data.get('password')) {
|
12 |
+
throw error(400, 'Pas de login renseigné');
|
13 |
+
}
|
14 |
+
|
15 |
+
const email = data.get('email')!.toString().trim();
|
16 |
|
17 |
const user = await users.findOne({ email }, { collation: { locale: 'en', strength: 1 } });
|
18 |
|
|
|
20 |
throw error(404, "Utilisateur non trouvé pour l'email: " + email);
|
21 |
}
|
22 |
|
23 |
+
const password = data.get('password')!.toString().trim();
|
24 |
|
25 |
if (!(await bcrypt.compare(password as string, user.hash))) {
|
26 |
throw error(401, 'Mauvais mot de passe');
|
|
|
38 |
sameSite: 'lax',
|
39 |
secure: true,
|
40 |
httpOnly: true,
|
41 |
+
expires: addYears(new Date(), 3)
|
42 |
});
|
43 |
|
44 |
if (event.url.searchParams.get('suivant')) {
|
45 |
+
throw redirect(303, event.url.searchParams.get('suivant')!);
|
46 |
}
|
47 |
|
48 |
return { success: true };
|
src/routes/deconnexion/+page.server.ts
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { redirect } from '@sveltejs/kit';
|
2 |
+
import type { Actions } from './$types';
|
3 |
+
|
4 |
+
export const actions: Actions = {
|
5 |
+
default: async (event) => {
|
6 |
+
throw redirect(303, event.request.headers.get('referer') ?? '/');
|
7 |
+
}
|
8 |
+
};
|