feat: Registration page
This commit is contained in:
@@ -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}
|
||||||
|
@@ -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();
|
||||||
|
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
export const publicPaths = [
|
export const publicPaths = [
|
||||||
'/login',
|
'/login',
|
||||||
|
'/register',
|
||||||
'/reset',
|
'/reset',
|
||||||
'/reset/password',
|
'/reset/password',
|
||||||
'/webhooks/success',
|
'/webhooks/success',
|
||||||
|
@@ -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
|
||||||
|
@@ -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>
|
||||||
|
103
src/routes/register/index.svelte
Normal file
103
src/routes/register/index.svelte
Normal 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>
|
11
src/routes/register/index.ts
Normal file
11
src/routes/register/index.ts
Normal 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);
|
||||||
|
}
|
||||||
|
};
|
@@ -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 {
|
||||||
|
Reference in New Issue
Block a user