Start to read the api

This commit is contained in:
Clément Le Bihan
2024-05-05 16:32:58 +02:00
parent 625c6a67e2
commit ac7e9f1a22
7 changed files with 219 additions and 16 deletions
+12
View File
@@ -31,5 +31,17 @@ services:
timeout: 5s
retries: 5
web:
build:
context: ./web
dockerfile: Dockerfile.dev
restart: on-failure
volumes:
- ./web:/app
ports:
- 5173:5173
environment:
- API_URL=http://api:1597
volumes:
db:
+12
View File
@@ -0,0 +1,12 @@
FROM node:22-alpine
WORKDIR /app
COPY package.json .
COPY package-lock.json .
RUN npm install
COPY . .
CMD ["npm", "run", "dev", "--", "--host"]
+37
View File
@@ -0,0 +1,37 @@
import { env } from '$env/dynamic/private';
import type { Post } from '$lib/types';
export async function login(email: string, password: string) {
const r = await fetch(env.API_URL + '/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ email, password })
});
const j = await r.json();
return j.token as string;
}
export async function register(email: string, username: string, password: string) {
const r = await fetch(env.API_URL + '/register', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ email, password, name: username })
});
const j = await r.json();
return j.token as string;
}
export async function getPosts(token?: string) {
const opts = {
headers: {
Authorization: `Bearer ${token}`
}
}
const r = await fetch(env.API_URL + '/entries', token ? opts : undefined);
const j = await r.json();
return j.posts as Post[];
}
+1 -1
View File
@@ -7,7 +7,7 @@ export type Post = {
link: string;
date: Date;
author?: string;
authors?: string[];
isRead: boolean;
isBookmarked: boolean;
isIgnored: boolean;
+4 -2
View File
@@ -1,4 +1,6 @@
import type { Post, Feed } from "$lib/types";
import { getPosts } from "$lib/server/api";
import { get } from "svelte/store";
const feeds: Feed[] = [
{
@@ -41,7 +43,7 @@ const posts: Post[] = [
isBookmarked: false,
isIgnored: false,
isReadLater: false,
author: "John Doe",
authors: ["John Doe"],
feed: feeds[0],
},
{
@@ -60,7 +62,7 @@ const posts: Post[] = [
export function load() {
return {
posts,
posts: getPosts(),
feeds
};
}
+37
View File
@@ -0,0 +1,37 @@
import { fail } from '@sveltejs/kit';
import { login, register } from '$lib/server/api.js';
export const actions = {
connect: async ({ cookies, request }) => {
const data = await request.formData();
const email = data.get('email');
const password = data.get('password');
const token = await login(email as any, password as any);
return {
token
};
},
register: async ({ cookies, request }) => {
const data = await request.formData();
const email = data.get('email');
const username = data.get('username');
const password = data.get('password');
const passwordRepeat = data.get('password-repeat');
console.log("register", email, username, password, passwordRepeat);
if (password !== passwordRepeat) {
return fail(400, {
data: {
email
},
errors: {
password: 'Passwords do not match'
}
})
}
const token = await register(email as any, username as any, password as any);
return {
token
};
},
}
+116 -13
View File
@@ -8,9 +8,19 @@
Heading,
Secondary,
P,
Helper,
Span,
Spinner,
} from "flowbite-svelte";
import { EnvelopeOutline, LockOutline } from "flowbite-svelte-icons";
import {
EnvelopeOutline,
LockOutline,
UserOutline,
} from "flowbite-svelte-icons";
import { enhance } from "$app/forms";
export let form;
let registering = false;
let connecting = false;
</script>
<div class="text-center mb-10">
@@ -21,10 +31,20 @@
>
Welcome to <Span gradient>Vex</Span>
</Heading>
<P class="mb-4 w-full text-center">The best place to share your thoughts with the world</P>
<P class="mb-4 w-full text-center">The best place to gather your feeds</P>
</div>
<div class="main">
<form>
<form
method="POST"
action="?/connect"
use:enhance={() => {
connecting = true;
return async ({ update }) => {
await update();
connecting = false;
};
}}
>
<Heading tag="h3" class="mb-4">Login</Heading>
<div>
<Label for="email" class="block mb-2">Email</Label>
@@ -32,7 +52,13 @@
<InputAddon>
<EnvelopeOutline class="w-4 h-4 text-gray-500 dark:text-gray-400" />
</InputAddon>
<Input id="email" type="email" placeholder="name@flowbite.com"></Input>
<Input
id="email"
type="email"
name="email"
placeholder="name@flowbite.com"
required
></Input>
</ButtonGroup>
</div>
<div>
@@ -41,44 +67,121 @@
<InputAddon>
<LockOutline class="w-4 h-4 text-gray-500 dark:text-gray-400" />
</InputAddon>
<Input id="password" type="password"></Input>
<Input id="password" type="password" name="password" required></Input>
</ButtonGroup>
</div>
<div>
<Button type="submit" color="primary" class="w-full">Connect</Button>
<Button
type="submit"
color="primary"
disabled={connecting}
class="w-full"
>
{#if connecting}
<Spinner class="me-3" size="4" color="white" />
{/if}
Connect</Button
>
</div>
</form>
<form>
<form
method="POST"
action="?/register"
use:enhance={() => {
registering = true;
return async ({ update }) => {
await update();
registering = false;
};
}}
>
<Heading tag="h3" class="mb-4">Welcome</Heading>
<div>
<Label for="username" class="block mb-2">Your Username</Label>
<ButtonGroup class="w-full">
<InputAddon>
<UserOutline class="w-4 h-4 text-gray-500 dark:text-gray-400" />
</InputAddon>
<Input
id="username"
type="text"
name="username"
placeholder="Jean luc"
required
></Input>
</ButtonGroup>
</div>
<div>
<Label for="email" class="block mb-2">Your Email</Label>
<ButtonGroup class="w-full">
<InputAddon>
<EnvelopeOutline class="w-4 h-4 text-gray-500 dark:text-gray-400" />
</InputAddon>
<Input id="email" type="email" placeholder="name@flowbite.com"></Input>
<Input
id="email"
type="email"
name="email"
placeholder="name@flowbite.com"
value={form?.data?.email}
required
></Input>
</ButtonGroup>
</div>
<div>
<Label for="password" class="block mb-2">Your Password</Label>
<Label
for="password"
class="block mb-2"
color={form?.errors?.password ? "red" : undefined}>Your Password</Label
>
<ButtonGroup class="w-full">
<InputAddon>
<LockOutline class="w-4 h-4 text-gray-500 dark:text-gray-400" />
</InputAddon>
<Input id="password" type="password"></Input>
<Input
color={form?.errors?.password ? "red" : undefined}
id="password"
name="password"
type="password"
required
></Input>
</ButtonGroup>
</div>
<div>
<Label for="password-repeat" class="block mb-2">Repeat Password</Label>
<Label
for="password-repeat"
color={form?.errors?.password ? "red" : undefined}
class="block mb-2">Repeat Password</Label
>
<ButtonGroup class="w-full">
<InputAddon>
<LockOutline class="w-4 h-4 text-gray-500 dark:text-gray-400" />
</InputAddon>
<Input id="password-repeat" type="password"></Input>
<Input
id="password-repeat"
name="password-repeat"
type="password"
color={form?.errors?.password ? "red" : undefined}
required
></Input>
</ButtonGroup>
{#if form?.errors?.password}
<Helper class="mt-2" color="red"
><span class="font-medium">Invalid!:</span> Passwords do not match</Helper
>
{/if}
</div>
<div>
<Button type="submit" color="primary" class="w-full">Register</Button>
<Button
type="submit"
color="primary"
disabled={registering}
class="w-full"
>
{#if registering}
<Spinner class="me-3" size="4" color="white" />
{/if}
Register</Button
>
</div>
</form>
</div>