login kept in memory

This commit is contained in:
Yanis Rigaudeau 2022-10-21 00:02:00 +02:00
parent ae0eb2c65a
commit 3c5b60e1aa
Signed by: yanis
GPG Key ID: 4DD2841DF1C94D83
16 changed files with 160 additions and 57 deletions

View File

@ -2,6 +2,7 @@ import { readFileSync } from 'fs';
export type ServerConfig = { export type ServerConfig = {
port: number; port: number;
origin: string[];
}; };
export type MongoConfig = { export type MongoConfig = {

View File

@ -16,10 +16,21 @@ class Server {
this.app = express(); this.app = express();
this.app.use(express.json()); this.app.use(express.json());
this.app.use(cors()); this.app.use(
cors({
maxAge: 86400,
credentials: true,
origin: this.config.origin,
}),
);
this.app.use(RequestId()); this.app.use(RequestId());
this.app.use( this.app.use(
session({ secret: randomUUID(), cookie: { maxAge: 1000 * 3600 * 24 } }), session({
secret: randomUUID(),
cookie: { maxAge: 1000 * 3600 * 24 },
resave: false,
saveUninitialized: false,
}),
); );
//this.app.use(checkPermissions()); //this.app.use(checkPermissions());
this.app.use(getRoutes(services)); this.app.use(getRoutes(services));

View File

@ -27,7 +27,7 @@ function LogoutHandler(services: Services): RequestHandler {
req.session.user = null; req.session.user = null;
res.status(204).send(); res.status(204).send();
} else { } else {
next({ message: 'not logged in' }); next({ status: 401, message: 'not logged in' });
} }
}; };
} }

View File

@ -38,7 +38,7 @@ function serve() {
export default { export default {
input: 'src/main.ts', input: 'src/main.ts',
output: { output: {
sourcemap: true, sourcemap: !production,
format: 'iife', format: 'iife',
name: 'app', name: 'app',
file: 'public/build/bundle.js', file: 'public/build/bundle.js',
@ -73,6 +73,7 @@ export default {
}), }),
commonjs(), commonjs(),
replace({ replace({
preventAssignment: true,
'process.env.APIURL': production ? '"/api"' : '"http://localhost:8000"', 'process.env.APIURL': production ? '"/api"' : '"http://localhost:8000"',
'process.env.CORS': production ? 'false' : 'true', 'process.env.CORS': production ? 'false' : 'true',
}), }),

View File

@ -4,6 +4,7 @@
import Test3D from './pages/test3D.svelte'; import Test3D from './pages/test3D.svelte';
import Login from './pages/Login.svelte'; import Login from './pages/Login.svelte';
import Profile from './pages/Profile.svelte';
</script> </script>
<Router> <Router>
@ -13,4 +14,7 @@
<Route path="/login"> <Route path="/login">
<Login /> <Login />
</Route> </Route>
<Route path="/profile">
<Profile />
</Route>
</Router> </Router>

View File

@ -0,0 +1,55 @@
<script lang="ts">
import { Link } from 'svelte-navigator';
import { currentUser } from '../store/user';
</script>
<nav class="border row">
{#if $currentUser === null}
<Link to="/" class="col-fill col">
<div class="nav-button" id="home" />
</Link>
<Link to="/login" class="col-fill col">
<div class="nav-button" id="login" />
</Link>
{:else}
<Link to="/" class="col-fill col">
<div class="nav-button" id="home" />
</Link>
<Link to="/profile" class="col-fill col">
<div class="nav-button" id="profile" />
</Link>
<Link to="/test" class="col-fill col">
<div class="nav-button" id="test" />
</Link>
{/if}
</nav>
<style>
nav {
position: absolute;
bottom: 0;
margin: 3px;
left: 0;
right: 0;
width: auto;
}
div.nav-button {
height: 30px;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
div.nav-button#home {
background-image: url('/images/mosaic-of-four-hand-drawn-squares-tiles.png');
}
div.nav-button#login {
background-image: url('/images/next-user-hand-drawn-interface-symbol.png');
}
div.nav-button#profile {
background-image: url('/images/id-hand-drawn-symbol.png');
}
</style>

View File

@ -1,38 +0,0 @@
<script lang="ts">
import { Link } from 'svelte-navigator';
</script>
<nav class="border row">
<Link to="/" class="col-fill col">
<div class="nav-button" id="home" />
</Link>
<Link to="/login" class="col-fill col">
<div class="nav-button" id="login" />
</Link>
</nav>
<style>
nav {
position: absolute;
bottom: 0;
margin: 3px;
left: 0;
right: 0;
width: auto;
}
div.nav-button {
height: 50px;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
div.nav-button#home {
background-image: url('/images/mosaic-of-four-hand-drawn-squares-tiles.png');
}
div.nav-button#login {
background-image: url('/images/next-user-hand-drawn-interface-symbol.png');
}
</style>

View File

@ -0,0 +1,19 @@
export function save(key: string, data: unknown) {
if (data) {
localStorage.setItem(key, JSON.stringify(data));
} else {
remove(key);
}
}
export function remove(key: string) {
localStorage.removeItem(key);
}
export function read<T>(key: string) {
const data = localStorage.getItem(key);
if (data) {
return JSON.parse(data) as T;
}
return null;
}

View File

@ -8,7 +8,10 @@ export async function get<T>(route: string): Promise<T | null> {
return request<T>('GET', route); return request<T>('GET', route);
} }
export async function post<T>(route: string, data: unknown): Promise<T | null> { export async function post<T>(
route: string,
data?: unknown,
): Promise<T | null> {
return request<T>('POST', route, data); return request<T>('POST', route, data);
} }
@ -25,12 +28,13 @@ async function request<T>(
headers: { headers: {
'content-type': 'application/json', 'content-type': 'application/json',
}, },
credentials: process.env.CORS ? 'include' : 'same-origin',
mode: process.env.CORS ? 'cors' : 'same-origin', mode: process.env.CORS ? 'cors' : 'same-origin',
method: method, method: method,
body: data ? JSON.stringify(data) : null, body: data ? JSON.stringify(data) : null,
}); });
if (response.ok) { if (response.ok && response.status !== 204) {
return response.json() as T; return response.json() as T;
} }
return null; return null;

View File

@ -1,8 +1,22 @@
import type { LoginUserBody, UserInfo } from '@core'; import type { CreateUserBody, LoginUserBody, UserInfo } from '@core';
import { post } from './request'; import { post, get } from './request';
import { currentUser } from '../store/user'; import { currentUser } from '../store/user';
export async function login(raw: LoginUserBody) { export async function login(raw: LoginUserBody) {
const user = await post<UserInfo>('/user/login', raw); const user = await post<UserInfo>('/user/login', raw);
currentUser.set(user); currentUser.set(user);
return user;
}
export async function logout() {
await post('/user/logout');
currentUser.set(null);
}
export async function read(uuid: string) {
const user = await get<UserInfo>(`/user/read/${uuid}`);
}
export async function create(raw: CreateUserBody) {
const user = await post<UserInfo>('/user/create', raw);
} }

View File

@ -2,7 +2,6 @@ import App from './App.svelte';
const app = new App({ const app = new App({
target: document.body, target: document.body,
props: {},
}); });
export default app; export default app;

View File

@ -1,14 +1,23 @@
<script lang="ts"> <script lang="ts">
import type { LoginUserBody } from '@core'; import type { LoginUserBody } from '@core';
import NavBar from '../components/navBar.svelte'; import NavBar from '../components/NavBar.svelte';
import { Form, Input, Button } from 'spaper'; import { Form, Input, Button } from 'spaper';
import { login } from '../functions/user'; import { login } from '../functions/user';
import { currentUser } from '../store/user'; import { useNavigate } from 'svelte-navigator';
const user: LoginUserBody = { const user: LoginUserBody = {
username: '', username: '',
password: '', password: '',
}; };
const navigate = useNavigate();
const loginHandler = async () => {
const result = await login(user);
if (result) {
navigate('/');
}
};
</script> </script>
<div class="row" id="container"> <div class="row" id="container">
@ -16,25 +25,23 @@
<Input <Input
placeholder="username" placeholder="username"
class="row col-8" class="row col-8"
autocomplete
bind:value={user.username} bind:value={user.username}
/> />
<Input <Input
placeholder="password" placeholder="password"
type="password" type="password"
autocomplete
class="row col-8" class="row col-8"
bind:value={user.password} bind:value={user.password}
/> />
<Button <Button
type="secondary" type="secondary"
class="row sm-2 col-4" class="row sm-2 col-4"
block block
on:click={() => login(user)}>Sign in</Button on:click={() => loginHandler()}>Sign in</Button
> >
</Form> </Form>
{$currentUser?.uuid}
{$currentUser?.username}
{$currentUser?.role}
</div> </div>
<NavBar /> <NavBar />

View File

@ -0,0 +1,23 @@
<script lang="ts">
import NavBar from '../components/NavBar.svelte';
import { currentUser } from '../store/user';
import { Button } from 'spaper';
import { logout } from '../functions/user';
import { useNavigate } from 'svelte-navigator';
const navigate = useNavigate();
const logoutHandler = async () => {
await logout();
navigate('/');
};
</script>
<Button type="danger" block on:click={() => logoutHandler()}>Logout</Button>
{$currentUser?.uuid}
{$currentUser?.username}
{$currentUser?.role}
<NavBar />
<style>
</style>

View File

@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import NavBar from '../components/navBar.svelte'; import NavBar from '../components/NavBar.svelte';
import { beforeUpdate, afterUpdate, onMount, onDestroy } from 'svelte'; import { beforeUpdate, afterUpdate, onMount, onDestroy } from 'svelte';
import * as Three from 'three'; import * as Three from 'three';

View File

@ -1,4 +1,8 @@
import { writable } from 'svelte/store'; import { writable } from 'svelte/store';
import type { UserInfo } from '@core'; import type { UserInfo } from '@core';
import { save, read } from '../functions/localStorage';
export const currentUser = writable<UserInfo | null>(null); export const currentUser = writable<UserInfo | null>(read<UserInfo>('user'));
currentUser.subscribe((user) => {
save('user', user);
});

View File

@ -1,7 +1,6 @@
{ {
"extends": "@tsconfig/svelte/tsconfig.json", "extends": "@tsconfig/svelte/tsconfig.json",
"compilerOptions": { "compilerOptions": {
"sourceMap": false,
"strictNullChecks": true, "strictNullChecks": true,
"baseUrl": "./src", "baseUrl": "./src",
"paths": { "paths": {