feat: Registration page

This commit is contained in:
Andras Bacsai
2022-02-24 09:32:34 +01:00
parent c9c003dc9b
commit a5ecff24a3
8 changed files with 143 additions and 7 deletions

View File

@@ -31,6 +31,8 @@
<textarea <textarea
rows="5" rows="5"
class={disabledClass} class={disabledClass}
class:pr-10={true}
class:pr-20={value && isHttps}
{placeholder} {placeholder}
type="text" type="text"
{id} {id}
@@ -44,6 +46,8 @@
<input <input
class={disabledClass} class={disabledClass}
type="text" type="text"
class:pr-10={true}
class:pr-20={value && isHttps}
{id} {id}
{name} {name}
{required} {required}
@@ -57,6 +61,8 @@
{:else} {:else}
<input <input
class={disabledClass} class={disabledClass}
class:pr-10={true}
class:pr-20={value && isHttps}
type="password" type="password"
{id} {id}
{name} {name}

View File

@@ -10,13 +10,18 @@ export async function hashPassword(password: string) {
const saltRounds = 15; const saltRounds = 15;
return bcrypt.hash(password, saltRounds); return bcrypt.hash(password, saltRounds);
} }
export async function login({ email, password }) { export async function login({ email, password, isLogin }) {
const users = await prisma.user.count(); const users = await prisma.user.count();
const userFound = await prisma.user.findUnique({ const userFound = await prisma.user.findUnique({
where: { email }, where: { email },
include: { teams: true, permission: true }, include: { teams: true, permission: true },
rejectOnNotFound: false rejectOnNotFound: false
}); });
if (!userFound && isLogin) {
throw {
error: 'Wrong password or email address.'
};
}
// Registration disabled if database is not seeded properly // Registration disabled if database is not seeded properly
const { isRegistrationEnabled, id } = await db.listSettings(); const { isRegistrationEnabled, id } = await db.listSettings();

View File

@@ -1,5 +1,6 @@
export const publicPaths = [ export const publicPaths = [
'/login', '/login',
'/register',
'/reset', '/reset',
'/reset/password', '/reset/password',
'/webhooks/success', '/webhooks/success',

View File

@@ -4,10 +4,10 @@ import { ErrorHandler } from '$lib/database';
import type { RequestHandler } from '@sveltejs/kit'; import type { RequestHandler } from '@sveltejs/kit';
export const post: RequestHandler = async (event) => { export const post: RequestHandler = async (event) => {
const { email, password } = await event.request.json(); const { email, password, isLogin } = await event.request.json();
try { try {
const { body } = await db.login({ email, password }); const { body } = await db.login({ email, password, isLogin });
event.locals.session.data = body; event.locals.session.data = body;
return { return {
status: 200 status: 200

View File

@@ -18,7 +18,11 @@
async function handleSubmit() { async function handleSubmit() {
loading = true; loading = true;
try { try {
const { teamId } = await post(`/login.json`, { email: email.toLowerCase(), password }); const { teamId } = await post(`/login.json`, {
email: email.toLowerCase(),
password,
isLogin: true
});
if (teamId === '0') { if (teamId === '0') {
window.location.replace('/settings'); window.location.replace('/settings');
} else { } else {
@@ -67,7 +71,13 @@
class:text-stone-600={loading} class:text-stone-600={loading}
class:bg-coollabs={!loading}>{loading ? 'Authenticating...' : 'Login'}</button class:bg-coollabs={!loading}>{loading ? 'Authenticating...' : 'Login'}</button
> >
<button on:click|preventDefault={() => goto('/reset')}>Reset password</button> <button
on:click|preventDefault={() => goto('/register')}
class="hover:opacity-90 text-white">Register</button
>
<button class="bg-transparent" on:click|preventDefault={() => goto('/reset')}
>Reset password</button
>
</div> </div>
</form> </form>
</div> </div>

View File

@@ -0,0 +1,103 @@
<script lang="ts">
export let userCount: number;
import { browser } from '$app/env';
import { goto } from '$app/navigation';
import { session } from '$app/stores';
import { post } from '$lib/api';
import { errorNotification } from '$lib/form';
import { onMount } from 'svelte';
let loading = false;
let emailEl;
let email, password, passwordCheck;
if (browser && $session.userId) {
goto('/');
}
onMount(() => {
emailEl.focus();
});
async function handleSubmit() {
if (password !== passwordCheck) {
return errorNotification('Passwords do not match.');
}
loading = true;
try {
await post(`/login.json`, {
email: email.toLowerCase(),
password,
isLogin: false
});
return window.location.replace('/');
} catch ({ error }) {
return errorNotification(error);
} finally {
loading = false;
}
}
</script>
<div class="icons fixed top-0 left-0 m-3 cursor-pointer" on:click={() => goto('/')}>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<line x1="5" y1="12" x2="19" y2="12" />
<line x1="5" y1="12" x2="11" y2="18" />
<line x1="5" y1="12" x2="11" y2="6" />
</svg>
</div>
<div class="flex h-screen flex-col items-center justify-center">
{#if $session.userId}
<div class="flex justify-center px-4 text-xl font-bold">Already logged in...</div>
{:else}
<div class="flex justify-center px-4">
<form on:submit|preventDefault={handleSubmit} class="flex flex-col py-4 space-y-2">
<div class="text-6xl font-bold border-gradient w-48 mx-auto border-b-4">Coolify</div>
<div class="text-xs text-center font-bold pb-10">v{$session.version}</div>
<input
type="email"
name="email"
placeholder="Email"
autocomplete="off"
required
bind:this={emailEl}
bind:value={email}
/>
<input
type="password"
name="password"
placeholder="Password"
bind:value={password}
required
/>
<input
type="password"
name="passwordCheck"
placeholder="Password again"
bind:value={passwordCheck}
required
/>
<div class="flex space-x-2 h-8 items-center justify-center pt-8">
<button type="submit" class="hover:bg-coollabs-100 text-white bg-coollabs"
>Register</button
>
</div>
</form>
</div>
{#if userCount === 0}
<div class="pt-5">
You are registering the first user. It will be the administrator of your Coolify instance.
</div>
{/if}
{/if}
</div>

View File

@@ -0,0 +1,11 @@
import * as db from '$lib/database';
import { ErrorHandler } from '$lib/database';
import type { RequestHandler } from '@sveltejs/kit';
export const get: RequestHandler = async () => {
try {
return { status: 200, body: { userCount: await db.prisma.user.count() } };
} catch (error) {
return ErrorHandler(error);
}
};

View File

@@ -35,10 +35,10 @@ main,
} }
input { input {
@apply h-12 w-96 rounded border border-transparent bg-transparent bg-coolgray-200 p-2 pr-20 text-xs tracking-tight text-white placeholder-stone-600 outline-none transition duration-150 hover:bg-coolgray-500 focus:bg-coolgray-500 disabled:border disabled:border-dashed disabled:border-coolgray-300 disabled:bg-transparent md:text-sm; @apply h-12 w-96 rounded border border-transparent bg-transparent bg-coolgray-200 p-2 text-xs tracking-tight text-white placeholder-stone-600 outline-none transition duration-150 hover:bg-coolgray-500 focus:bg-coolgray-500 disabled:border disabled:border-dashed disabled:border-coolgray-300 disabled:bg-transparent md:text-sm;
} }
textarea { textarea {
@apply min-w-[24rem] rounded border border-transparent bg-transparent bg-coolgray-200 p-2 pr-20 text-xs tracking-tight text-white placeholder-stone-600 outline-none transition duration-150 hover:bg-coolgray-500 focus:bg-coolgray-500 disabled:border disabled:border-dashed disabled:border-coolgray-300 disabled:bg-transparent md:text-sm; @apply min-w-[24rem] rounded border border-transparent bg-transparent bg-coolgray-200 p-2 text-xs tracking-tight text-white placeholder-stone-600 outline-none transition duration-150 hover:bg-coolgray-500 focus:bg-coolgray-500 disabled:border disabled:border-dashed disabled:border-coolgray-300 disabled:bg-transparent md:text-sm;
} }
select { select {